branch | jv |
changeset 3873 | 707275c1f86d |
parent 3841 | a22f33410bdf |
parent 3861 | 214e93764392 |
child 3874 | 4f9db2d4c2b7 |
3850:ca4ea3855eef | 3873:707275c1f86d |
---|---|
54 ! ! |
54 ! ! |
55 |
55 |
56 !Explainer class methodsFor:'explaining'! |
56 !Explainer class methodsFor:'explaining'! |
57 |
57 |
58 explainLiteralNode:node in:code forClass:cls short:short interval:intervalIfKnown |
58 explainLiteralNode:node in:code forClass:cls short:short interval:intervalIfKnown |
59 |expl literalValue findInnerMost elementIndex codeOfCharacterBeforeCursor| |
59 |expl literalValue literalsClass findInnerMost elementIndex codeOfCharacterBeforeCursor| |
60 |
60 |
61 literalValue := node value. |
61 literalValue := node value. |
62 expl := literalValue class name "allBold" , '-constant'. |
62 literalsClass := literalValue class. |
63 expl := (self asClassLink:literalsClass name "allBold") , '-constant'. |
|
63 |
64 |
64 (literalValue isInteger) ifTrue:[ |
65 (literalValue isInteger) ifTrue:[ |
65 (literalValue ~~ 0 and:[literalValue ~~ 1]) ifTrue:[ |
66 (literalValue ~~ 0 and:[literalValue ~~ 1]) ifTrue:[ |
66 expl := expl , ' ('. |
67 expl := expl , ' ('. |
67 #(2 10 16) with:#('binary: ' 'decimal: ' 'hex: ') do:[:base :baseExpl | |
68 #(2 10 16) with:#('binary: ' 'decimal: ' 'hex: ') do:[:base :baseExpl | |
144 explainMessageNode:node in:code forClass:cls short:short interval:intervalIfKnown |
145 explainMessageNode:node in:code forClass:cls short:short interval:intervalIfKnown |
145 "answer a string which explains node" |
146 "answer a string which explains node" |
146 |
147 |
147 |receiver nm srchClass selector selectorString implClass |
148 |receiver nm srchClass selector selectorString implClass |
148 boldSelectorString globalValue recClassSet |
149 boldSelectorString globalValue recClassSet |
149 implMethod implMethodComment info implMethods comments definer |
150 implMethod implMethodComment info definer |
150 instances classesOfInstVars implementingClasses canBeNil |
151 instances classesOfInstVars implementingClasses canBeNil |
151 bestMatches hint| |
152 bestMatches hint| |
152 |
153 |
153 selector := node buildSelectorString. |
154 selector := node buildSelectorString. |
154 selectorString := selector printString contractTo:30. |
155 selectorString := selector printString contractTo:50. |
155 selector := selector asSymbolIfInterned. "/ avoid creating new symbols. |
156 selector := selector asSymbolIfInterned. "/ avoid creating new symbols. |
156 selector isNil ifTrue:[ |
157 selector isNil ifTrue:[ |
157 ^ '%1 is NOWHERE impemented.' bindWith:boldSelectorString |
158 ^ '%1 is NOWHERE impemented.' bindWith:boldSelectorString |
158 ]. |
159 ]. |
159 |
160 |
160 selectorString := selectorString actionForAll:(self actionToBrowseImplementorsOf:selector). |
161 selectorString := self asLink:selectorString to:(self actionToBrowseImplementorsOf:selector). |
161 boldSelectorString := selectorString "allBold". |
162 boldSelectorString := selectorString "allBold". |
162 |
163 |
163 recClassSet := self guessPossibleImplementorClassesFor:(node receiver) in:code forClass:cls. |
164 recClassSet := self guessPossibleImplementorClassesFor:(node receiver) in:code forClass:cls. |
164 recClassSet size == 1 ifTrue:[ |
165 recClassSet size == 1 ifTrue:[ |
165 srchClass := recClassSet first. |
166 srchClass := recClassSet first. |
172 bindWith:selector "allBold" |
173 bindWith:selector "allBold" |
173 with:(implementingClasses size - 1) |
174 with:(implementingClasses size - 1) |
174 ]. |
175 ]. |
175 |
176 |
176 (#('self' 'super' 'true' 'false' 'thisContext') includes:selector) ifTrue:[ |
177 (#('self' 'super' 'true' 'false' 'thisContext') includes:selector) ifTrue:[ |
177 ^ ('NOT understood here: %1 (missing period after previous statement?)' bindWith:selector allBold),hint |
178 ^ ('NOT understood here: %1 (missing period after previous statement?)' |
178 ]. |
179 bindWith:selector allBold) |
179 |
180 ]. |
181 |
|
182 hint := ''. |
|
180 (recClassSet contains:[:cls | cls isMeta not and:[cls theMetaclass canUnderstand:selector]]) ifTrue:[ |
183 (recClassSet contains:[:cls | cls isMeta not and:[cls theMetaclass canUnderstand:selector]]) ifTrue:[ |
181 hint := '. But class understands it - did you mean "rcvr class ',selector,'..." ?'. |
184 hint := '. But class understands it - did you mean "rcvr class ',selector,'..." ?'. |
182 ]. |
185 ]. |
183 bestMatches := Parser findBestSelectorsFor:selector in:srchClass. |
186 bestMatches := Parser findBestSelectorsFor:selector in:srchClass. |
184 bestMatches size > 0 ifTrue:[ |
187 bestMatches size > 0 ifTrue:[ |
185 ^ ('NOT understood here: %1 (best guess is: "%2" from %3)' |
188 ^ ('NOT understood here: %1 (best guess is: "%2" from %3)' |
186 bindWith:selector allBold |
189 bindWith:selector allBold |
187 with:(bestMatches first "allBold") |
190 with:(bestMatches first "allBold") |
188 with:(srchClass whichClassIncludesSelector:bestMatches first) name) , (hint?'') |
191 with:(srchClass whichClassIncludesSelector:bestMatches first) name) , hint |
189 ]. |
192 ]. |
190 ^ ('NOT understood here: %1' bindWith:selector allBold),(hint ? '') |
193 ^ ('NOT understood here: %1' bindWith:selector allBold),hint |
191 ]. |
194 ]. |
192 ]. |
195 ]. |
193 |
196 |
194 implementingClasses isNil ifTrue:[ |
197 implementingClasses isNil ifTrue:[ |
195 receiver := node receiver. |
198 receiver := node receiver. |
232 srchClass notNil ifTrue:[ |
235 srchClass notNil ifTrue:[ |
233 implClass := srchClass whichClassIncludesSelector:selector. |
236 implClass := srchClass whichClassIncludesSelector:selector. |
234 implClass isNil ifTrue:[ |
237 implClass isNil ifTrue:[ |
235 ^ '%1 is NOT understood here.' bindWith:boldSelectorString |
238 ^ '%1 is NOT understood here.' bindWith:boldSelectorString |
236 ]. |
239 ]. |
237 |
240 implementingClasses := { implClass }. |
238 implMethod := implClass compiledMethodAt:selector. |
|
239 |
|
240 info := '%1 >> %2' bindWith:implClass name "allBold" with:selectorString "allBold". |
|
241 info := info actionForAll:(self actionToBrowseClass:implClass selector:selector). |
|
242 |
|
243 implMethodComment := self fetchCommentOfMethod:implMethod. |
|
244 implMethodComment notNil ifTrue:[ |
|
245 info := info , ' ' , implMethodComment. |
|
246 ]. |
|
247 ^ info |
|
248 ]. |
241 ]. |
249 implementingClasses isNil ifTrue:[ |
242 implementingClasses isNil ifTrue:[ |
250 implementingClasses := Smalltalk allImplementorsOf:selector |
243 implementingClasses := Smalltalk allImplementorsOf:selector |
251 ]. |
244 ]. |
252 ]. |
245 ]. |
253 |
246 |
254 implementingClasses size == 1 ifTrue:[ |
247 implementingClasses size == 1 ifTrue:[ |
255 |clsName| |
248 |clsName| |
249 |
|
256 implClass := implementingClasses anElement. |
250 implClass := implementingClasses anElement. |
257 implMethod := implClass compiledMethodAt:selector. |
251 implMethod := implClass compiledMethodAt:selector. |
258 clsName := implClass name. |
252 clsName := implClass name. |
259 clsName := clsName actionForAll:(self actionToBrowseClass:implClass selector:selector). |
253 clsName := self asLink:clsName to:(self actionToBrowseClass:implClass selector:selector). |
260 info := '%1 >> %2' bindWith:clsName "allBold" with:selectorString. |
254 info := '%1 >> %2' bindWith:clsName "allBold" with:selectorString. |
261 (srchClass isNil and:[(cls includesBehavior:implClass) not]) ifTrue:[ |
255 info := self asLink:info to:(self actionToBrowseClass:implClass selector:selector). |
262 "/ info := 'guess: ', info. |
256 |
263 info := info , ' (guess)'. |
257 implMethodComment := self fetchCommentOfMethod:implMethod. |
264 ]. |
258 implMethodComment notNil ifTrue:[ |
259 info := info , ' ' , implMethodComment. |
|
260 ]. |
|
261 ^ info |
|
262 "/ (srchClass isNil and:[(cls includesBehavior:implClass) not]) ifTrue:[ |
|
263 "/ "/ info := 'guess: ', info. |
|
264 "/ info := info , ' (guess)'. |
|
265 "/ ]. |
|
265 ] ifFalse:[ |
266 ] ifFalse:[ |
266 info := Explainer explainSelector:selector inClass:cls short:short. |
267 info := Explainer explainSelector:selector inClass:cls short:short. |
267 ]. |
268 ]. |
268 |
269 |
269 "/ implementingClasses notEmptyOrNil ifTrue:[ |
270 "/ implementingClasses notEmptyOrNil ifTrue:[ |
301 "Modified: / 30-04-2016 / 17:08:11 / cg" |
302 "Modified: / 30-04-2016 / 17:08:11 / cg" |
302 ! |
303 ! |
303 |
304 |
304 explainMethodNode:node in:code forClass:cls short:short interval:intervalIfKnown |
305 explainMethodNode:node in:code forClass:cls short:short interval:intervalIfKnown |
305 |srchClass selector selectorString implClass |
306 |srchClass selector selectorString implClass |
306 "sendingMethods numSendingMethods sendingClasses" boldSelectorString| |
307 "sendingMethods numSendingMethods sendingClasses" | |
307 |
308 |
308 selector := node selector. |
309 selector := node selector. |
309 selector := selector asSymbolIfInterned. "/ avoid creating new symbols. |
310 selector := selector asSymbolIfInterned. "/ avoid creating new symbols. |
310 selectorString := selector printString contractTo:30. |
311 selectorString := selector printString contractTo:50. |
311 boldSelectorString := selectorString "allBold". |
|
312 |
312 |
313 (srchClass := cls superclass) notNil ifTrue:[ |
313 (srchClass := cls superclass) notNil ifTrue:[ |
314 implClass := srchClass whichClassIncludesSelector:selector. |
314 implClass := srchClass whichClassIncludesSelector:selector. |
315 implClass notNil ifTrue:[ |
315 implClass notNil ifTrue:[ |
316 ^ '%1 hides implementation in %2.' |
316 ^ '%1 overrides implementation in %2.' |
317 bindWith:boldSelectorString |
317 bindWith:(self asLink:selectorString "allBold" to:(self actionToOpenMethodFinderFor:selector)) |
318 with:implClass name "allBold" |
318 with:(self asLink:implClass name "allBold" to:(self actionToBrowseClass:implClass selector:selector)) |
319 ]. |
319 ]. |
320 ]. |
320 ]. |
321 (cls includesSelector:selector) ifFalse:[ |
321 (cls includesSelector:selector) ifFalse:[ |
322 ^ '%1: a new method.' bindWith:boldSelectorString |
322 ^ '%1: a new method.' bindWith:selectorString "allBold" |
323 ]. |
323 ]. |
324 "/ |
324 "/ |
325 "/ sendingMethods := SystemBrowser |
325 "/ sendingMethods := SystemBrowser |
326 "/ allCallsOn:selector |
326 "/ allCallsOn:selector |
327 "/ in:(cls withAllSubclasses , cls allSubclasses) |
327 "/ in:(cls withAllSubclasses , cls allSubclasses) |
423 |
423 |
424 ^ nil |
424 ^ nil |
425 ! |
425 ! |
426 |
426 |
427 explainVariableNode:node in:code forClass:cls short:short interval:intervalIfKnown |
427 explainVariableNode:node in:code forClass:cls short:short interval:intervalIfKnown |
428 |expl nm nmBold definingNode namePart| |
428 |expl nm nmBold definingNode namePart argNode argClass argClassSet| |
429 |
429 |
430 nm := node name. |
430 nm := node name. |
431 |
431 |
432 (#( 'self' 'super' 'thisContext' 'here') includes:nm) ifTrue:[ |
432 (#( 'self' 'super' 'thisContext' 'here') includes:nm) ifTrue:[ |
433 ^ Explainer explainPseudoVariable:nm in:cls short:short |
433 ^ Explainer explainPseudoVariable:nm in:cls short:short |
437 |
437 |
438 definingNode := node whoDefines:nm. |
438 definingNode := node whoDefines:nm. |
439 definingNode notNil ifTrue:[ |
439 definingNode notNil ifTrue:[ |
440 namePart := '''' , nmBold , ''''. |
440 namePart := '''' , nmBold , ''''. |
441 definingNode isMethod ifTrue:[ |
441 definingNode isMethod ifTrue:[ |
442 (definingNode arguments contains:[:arg | arg name = nm]) ifTrue:[ |
442 argNode := definingNode arguments detect:[:arg | arg name = nm] ifNone:nil. |
443 expl := namePart , ' is a method argument.' |
443 argNode notNil ifTrue:[ |
444 expl := namePart , ' is a method argument.'. |
|
445 |
|
446 argClassSet := self guessPossibleImplementorClassesFor:argNode in:code forClass:cls. |
|
447 argClassSet size == 1 ifTrue:[ |
|
448 argClass := argClassSet first. |
|
449 ]. |
|
444 ]. |
450 ]. |
445 ]. |
451 ]. |
446 expl isNil ifTrue:[ |
452 expl isNil ifTrue:[ |
447 definingNode isBlock ifTrue:[ |
453 definingNode isBlock ifTrue:[ |
448 (definingNode arguments contains:[:arg | arg name = nm]) ifTrue:[ |
454 (definingNode arguments contains:[:arg | arg name = nm]) ifTrue:[ |
486 ! |
492 ! |
487 |
493 |
488 fetchCommentOfMethod:mthd |
494 fetchCommentOfMethod:mthd |
489 "retrieve the comment of a method (if possible and there is one; otherwise, return nil)" |
495 "retrieve the comment of a method (if possible and there is one; otherwise, return nil)" |
490 |
496 |
491 |methodComment lines| |
497 |windowGroup methodComment lines| |
492 |
498 |
493 "/ with wait cursor, because it accesses sourcecode (via SCM) |
499 "/ with wait cursor, because it accesses sourcecode (via SCM) |
494 WindowGroup activeGroup withWaitCursorDo:[ |
500 "/ however: this class is in libcomp (should be in libtool) |
501 "/ so check if WindowGroup (from libview) is present |
|
502 windowGroup := Smalltalk at:#WindowGroup. |
|
503 windowGroup isNil ifTrue:[ |
|
495 methodComment := mthd comment. |
504 methodComment := mthd comment. |
496 ]. |
505 ] ifFalse:[ |
506 windowGroup activeGroup withWaitCursorDo:[ |
|
507 methodComment := mthd comment. |
|
508 ]. |
|
509 ]. |
|
510 "/ Transcript showCR:methodComment. |
|
511 |
|
497 methodComment isEmptyOrNil ifTrue:[^ nil]. |
512 methodComment isEmptyOrNil ifTrue:[^ nil]. |
498 |
513 |
499 lines := methodComment asStringCollection. |
514 lines := methodComment asStringCollection. |
500 methodComment := lines first. |
515 methodComment := lines first. |
501 methodComment := methodComment withoutSeparators. |
516 methodComment := methodComment withoutSeparators. |
645 c := aClass whichClassDefinesInstVar:string. |
660 c := aClass whichClassDefinesInstVar:string. |
646 c notNil ifTrue:[ |
661 c notNil ifTrue:[ |
647 c isMeta ifTrue:[ |
662 c isMeta ifTrue:[ |
648 clsName := c theNonMetaclass name. |
663 clsName := c theNonMetaclass name. |
649 shortText ifTrue:[ |
664 shortText ifTrue:[ |
650 clsName := (clsName actionForAll:(self actionToBrowseClass:c selector:nil)). |
665 clsName := self asLink:clsName to:(self actionToBrowseClass:c). |
651 stringText := stringText , ': a class instVar in ' , clsName |
666 stringText := stringText , ': a class instVar in ' , clsName |
652 ] ifFalse:[ |
667 ] ifFalse:[ |
653 stringText := stringText, ': a class instance variable inherited from ' , clsName |
668 stringText := stringText, ': a class instance variable inherited from ' , clsName |
654 ]. |
669 ]. |
655 val := aClass theNonMetaclass instVarNamed:string. |
670 val := aClass theNonMetaclass instVarNamed:string. |
663 "classvars" |
678 "classvars" |
664 c := explainer inWhichClassIsClassVar:string. |
679 c := explainer inWhichClassIsClassVar:string. |
665 c notNil ifTrue:[ |
680 c notNil ifTrue:[ |
666 clsName := c name. |
681 clsName := c name. |
667 shortText ifTrue:[ |
682 shortText ifTrue:[ |
668 clsName := (clsName actionForAll:(self actionToBrowseClass:c selector:nil)). |
683 clsName := self asLink:clsName to:(self actionToBrowseClass:c). |
669 stringText := stringText , ': a classVar in ' , clsName |
684 stringText := stringText , ': a classVar in ' , clsName |
670 ] ifFalse:[ |
685 ] ifFalse:[ |
671 stringText := stringText , ': a class variable in ' , clsName |
686 stringText := stringText , ': a class variable in ' , clsName |
672 ]. |
687 ]. |
673 |
688 |
679 "private classes" |
694 "private classes" |
680 c := aClass theNonMetaclass. |
695 c := aClass theNonMetaclass. |
681 c privateClasses do:[:pClass | |
696 c privateClasses do:[:pClass | |
682 (pClass name = string |
697 (pClass name = string |
683 or:[pClass nameWithoutPrefix = string]) ifTrue:[ |
698 or:[pClass nameWithoutPrefix = string]) ifTrue:[ |
684 stringText := (stringText actionForAll:(self actionToBrowseClass:pClass selector:nil)). |
699 stringText := self asLink:stringText to:(self actionToBrowseClass:pClass). |
685 stringText := stringText , ': a private class in ''' , c name , '''.'. |
700 stringText := stringText , ': a private class in ''' , c name , '''.'. |
686 shortText ifFalse:[ |
701 shortText ifFalse:[ |
687 stringText := (stringText , '\\It is only visible locally.') withCRs |
702 stringText := (stringText , '\\It is only visible locally.') withCRs |
688 ]. |
703 ]. |
689 ^ stringText withCRs |
704 ^ stringText withCRs |
700 sharedPool isSharedPool ifFalse:[ |
715 sharedPool isSharedPool ifFalse:[ |
701 ^ 'oops - not a shared pool: ',eachPoolName |
716 ^ 'oops - not a shared pool: ',eachPoolName |
702 ]. |
717 ]. |
703 (sharedPool includesKey:sharedPoolSym) ifTrue:[ |
718 (sharedPool includesKey:sharedPoolSym) ifTrue:[ |
704 poolName := sharedPool name. |
719 poolName := sharedPool name. |
705 poolName := (poolName actionForAll:(self actionToBrowseClass:sharedPool selector:nil)). |
720 poolName := self asLink:poolName to:(self actionToBrowseClass:sharedPool). |
706 stringText := stringText , ': a pool variable in ',poolName. |
721 stringText := stringText , ': a pool variable in ',poolName. |
707 val := sharedPool at:sharedPoolSym. |
722 val := sharedPool at:sharedPoolSym. |
708 valString := self valueStringFor:val. |
723 valString := self valueStringFor:val. |
709 ^ stringText , ' (' , valString , ').' |
724 ^ stringText , ' (' , valString , ').' |
710 ]. |
725 ]. |
715 "namespace & global variables" |
730 "namespace & global variables" |
716 (spc := aClass nameSpace) notNil ifTrue:[ |
731 (spc := aClass nameSpace) notNil ifTrue:[ |
717 sym := (spc name , '::' , string) asSymbolIfInterned. |
732 sym := (spc name , '::' , string) asSymbolIfInterned. |
718 sym notNil ifTrue:[ |
733 sym notNil ifTrue:[ |
719 (cls := Smalltalk at:sym) isBehavior ifTrue:[ |
734 (cls := Smalltalk at:sym) isBehavior ifTrue:[ |
720 stringText := (stringText actionForAll:(self actionToBrowseClass:cls selector:nil)). |
735 stringText := self asLink:stringText to:(self actionToBrowseClass:cls). |
721 string := stringText , ': '. |
736 string := stringText , ': '. |
722 cls name = sym ifFalse:[ |
737 cls name = sym ifFalse:[ |
723 string := string , 'refers to ',cls name,', ' |
738 string := string , 'refers to ',cls name,', ' |
724 ]. |
739 ]. |
725 cls isSharedPool ifTrue:[ |
740 cls isSharedPool ifTrue:[ |
942 |
957 |
943 "Created: / 28-02-2012 / 10:44:55 / cg" |
958 "Created: / 28-02-2012 / 10:44:55 / cg" |
944 ! |
959 ! |
945 |
960 |
946 explainInstanceVariable:instVarName inClass:aClass short:shortText |
961 explainInstanceVariable:instVarName inClass:aClass short:shortText |
947 |template stringText setOfTypes typesDescription| |
962 |varNameInText classNameInText template stringText setOfTypes typesDescription| |
948 |
963 |
964 varNameInText := instVarName allBold. |
|
965 classNameInText := aClass name. |
|
966 |
|
949 shortText ifTrue:[ |
967 shortText ifTrue:[ |
950 template := '%1: an instVar in %2' |
968 template := '%1: an instVar in %2'. |
969 varNameInText := self asLink:varNameInText to:(self actionToBrowseInstvarRefsTo:instVarName inClass:aClass). |
|
970 classNameInText := self asLink:classNameInText to:(self actionToBrowseClass:aClass). |
|
951 ] ifFalse:[ |
971 ] ifFalse:[ |
952 template := '%1: an instance variable in %2' |
972 template := '%1: an instance variable in %2' |
953 ]. |
973 ]. |
954 stringText := template bindWith:instVarName allBold with:aClass name. |
974 stringText := template bindWith:varNameInText with:classNameInText. |
955 |
975 |
956 "/ look for instances |
976 "/ look for instances |
957 setOfTypes := IdentitySet new. |
977 setOfTypes := IdentitySet new. |
958 self addTypesOfInstvar:instVarName inClass:aClass fromExistingInstancesTo:setOfTypes. |
978 self addTypesOfInstvar:instVarName inClass:aClass fromExistingInstancesTo:setOfTypes. |
959 "/ look for assignments |
979 "/ look for assignments |
1132 shortText ifTrue:[ |
1152 shortText ifTrue:[ |
1133 tmp := ' is implemented in '. |
1153 tmp := ' is implemented in '. |
1134 ] ifFalse:[ |
1154 ] ifFalse:[ |
1135 tmp := ' is a selector implemented in '. |
1155 tmp := ' is a selector implemented in '. |
1136 ]. |
1156 ]. |
1137 s := "'#' ," string allBold. |
1157 s := string allBold. |
1138 s := s actionForAll:(self actionToBrowseImplementorsOf:selector). |
1158 count > 1 ifTrue:[ |
1159 s := self asLink:s to:(self actionToOpenMethodFinderFor:selector). |
|
1160 ] ifFalse:[ |
|
1161 s := self asLink:s to:(self actionToBrowseImplementorsOf:selector). |
|
1162 ]. |
|
1139 |
1163 |
1140 shortText ifTrue:[ |
1164 shortText ifTrue:[ |
1141 |typesDescription| |
1165 |typesDescription| |
1142 |
1166 |
1143 msg := s , tmp. |
1167 msg := s , tmp. |
1144 typesDescription := (self typeDescriptionFor:listOfImplementingClasses andSelector:selector). |
1168 typesDescription := self typeDescriptionFor:listOfImplementingClasses andSelector:selector wordBetween:'and'. |
1145 typesDescription notNil ifTrue:[ |
1169 typesDescription notNil ifTrue:[ |
1146 msg := msg,' (',typesDescription,')' |
1170 msg := msg,typesDescription |
1147 ]. |
1171 ]. |
1148 ] ifFalse:[ |
1172 ] ifFalse:[ |
1149 (count == 1) ifTrue:[ |
1173 (count == 1) ifTrue:[ |
1150 t := firstImplementingClassName. |
1174 t := firstImplementingClassName. |
1151 firstImplementingClass isMeta ifTrue:[ |
1175 firstImplementingClass isMeta ifTrue:[ |
1401 |superName| |
1425 |superName| |
1402 |
1426 |
1403 superName := aClass superclass name. |
1427 superName := aClass superclass name. |
1404 |
1428 |
1405 shortText ifTrue:[ |
1429 shortText ifTrue:[ |
1406 ^ '"super" - message lookup starts in superclass "%1" (call redefined method).' bindWith:superName |
1430 ^ '"super" - message lookup starts in superclass "%1" (call redefined method).' bindWith:(self asClassLink:superName) |
1407 ]. |
1431 ]. |
1408 |
1432 |
1409 ^ 'like "self", "','super'allBold,'" refers to the object which received the message. |
1433 ^ 'like "self", "','super'allBold,'" refers to the object which received the message. |
1410 |
1434 |
1411 However, when sending a message to "super", the search for methods |
1435 However, when sending a message to "super", the search for methods |
1421 |
1445 |
1422 ^ self explainSyntax:string short:false |
1446 ^ self explainSyntax:string short:false |
1423 ! |
1447 ! |
1424 |
1448 |
1425 explainSyntax:string short:shortText |
1449 explainSyntax:string short:shortText |
1426 "try syntax ...; return explanation or nil" |
1450 "try syntax ...; return explanation or nil. |
1451 This is meant for beginners..." |
|
1427 |
1452 |
1428 ((string = ':=') or:[string = '_']) ifTrue:[ |
1453 ((string = ':=') or:[string = '_']) ifTrue:[ |
1429 shortText ifTrue:[ |
1454 shortText ifTrue:[ |
1430 ^ '":=" - assign to variable on the left (syntax)'. |
1455 string = '_' ifTrue:[ |
1456 ^ '"_" - old style for assignment. Consider changing to ":=".' |
|
1457 ]. |
|
1458 ^ '":=" - assign to variable on the left (syntax).'. |
|
1431 ]. |
1459 ]. |
1432 |
1460 |
1433 ^ '<variable> := <expression> |
1461 ^ '<variable> := <expression> |
1434 |
1462 |
1435 ":=" and "_" (which is left-arrow in some fonts) mean assignment. |
1463 ":=" and "_" (which is left-arrow in some fonts) mean assignment. |
1470 "|" is also a selector understood by Booleans.' |
1498 "|" is also a selector understood by Booleans.' |
1471 ]. |
1499 ]. |
1472 |
1500 |
1473 (string startsWith:'$' ) ifTrue:[ |
1501 (string startsWith:'$' ) ifTrue:[ |
1474 shortText ifTrue:[ |
1502 shortText ifTrue:[ |
1475 ^ '"$x" - character literal (syntax)'. |
1503 ^ '"$x" - ',(self asClassLink:'Character'),' literal (syntax)'. |
1476 ]. |
1504 ]. |
1477 ^ 'is a Character literal constant. |
1505 ^ 'is a Character literal constant. |
1478 |
1506 |
1479 Character objects represent indivdual text cheracters in Unicode encoding. |
1507 Character objects represent indivdual text cheracters in Unicode encoding. |
1480 For example, $a is the character "a" with an encoding of 97 "hex: 16r61". |
1508 For example, $a is the character "a" with an encoding of 97 "hex: 16r61". |
1483 ]. |
1511 ]. |
1484 |
1512 |
1485 (string startsWith:'#' ) ifTrue:[ |
1513 (string startsWith:'#' ) ifTrue:[ |
1486 (string startsWith:'#(' ) ifTrue:[ |
1514 (string startsWith:'#(' ) ifTrue:[ |
1487 shortText ifTrue:[ |
1515 shortText ifTrue:[ |
1488 ^ '"#(..)" - array literal (syntax)'. |
1516 ^ '"#(..)" - ',(self asClassLink:'Array'),' literal (syntax)'. |
1489 ]. |
1517 ]. |
1490 ^ 'is a constant Array (literal). |
1518 ^ 'is a constant Array (literal). |
1491 |
1519 |
1492 The array-object is created at compilation time and a reference to this is |
1520 The array-object is created at compilation time and a reference to this is |
1493 used at execution time (thus, the same object is referred to every time). |
1521 used at execution time (thus, the same object is referred to every time). |
1497 element in an Array-constant).' |
1525 element in an Array-constant).' |
1498 ]. |
1526 ]. |
1499 |
1527 |
1500 (string startsWith:'#[') ifTrue:[ |
1528 (string startsWith:'#[') ifTrue:[ |
1501 shortText ifTrue:[ |
1529 shortText ifTrue:[ |
1502 ^ '"#[..]" - byteArray literal (syntax)'. |
1530 ^ '"#[..]" - ',(self asClassLink:'ByteArray'),' literal (syntax)'. |
1503 ]. |
1531 ]. |
1504 ^ 'is a constant ByteArray (literal). |
1532 ^ 'is a constant ByteArray (literal). |
1505 |
1533 |
1506 The elements of a constant ByteArray must be Integer constants in the range |
1534 The elements of a constant ByteArray must be Integer constants in the range |
1507 0 .. 255. |
1535 0 .. 255. |
1508 (notice, that not all Smalltalk implementations support constant ByteArrays).' |
1536 (notice, that not all Smalltalk implementations support constant ByteArrays).' |
1509 ]. |
1537 ]. |
1510 |
1538 |
1511 (string startsWith:'#''') ifTrue:[ |
1539 (string startsWith:'#''') ifTrue:[ |
1512 shortText ifTrue:[ |
1540 shortText ifTrue:[ |
1513 ^ '"#''..''" - symbol literal (syntax)'. |
1541 ^ '"#''..''" - ',(self asClassLink:'Symbol'),' literal (syntax)'. |
1514 ]. |
1542 ]. |
1515 ^ 'is a constant symbol containing non-alphanumeric characters. |
1543 ^ 'is a constant symbol containing non-alphanumeric characters. |
1516 |
1544 |
1517 Symbols are unique strings, meaning that there exists |
1545 Symbols are unique strings, meaning that there exists |
1518 exactly one instance of a given symbol. Therefore symbols can |
1546 exactly one instance of a given symbol. Therefore symbols can |
1519 be compared using == (identity compare) in addition to = (contents compare). |
1547 be compared using == (identity compare) in addition to = (contents compare). |
1520 Beside this, Symbols behave mostly like Strings but are immutable.' |
1548 Beside this, Symbols behave mostly like Strings but are immutable.' |
1521 ]. |
1549 ]. |
1522 |
1550 |
1523 shortText ifTrue:[ |
1551 shortText ifTrue:[ |
1524 ^ '"#.." - symbol literal (syntax)'. |
1552 ^ '"#.." - ',(self asClassLink:'Symbol'),' literal (syntax)'. |
1525 ]. |
1553 ]. |
1526 ^ 'is a constant symbol. |
1554 ^ 'is a constant symbol. |
1527 |
1555 |
1528 Symbols are unique strings, meaning that there exists |
1556 Symbols are unique strings, meaning that there exists |
1529 exactly one instance of a given symbol. Therefore symbols can |
1557 exactly one instance of a given symbol. Therefore symbols can |
1540 with unary messages preceeding binary messages, preceeding keyword mesages.' |
1568 with unary messages preceeding binary messages, preceeding keyword mesages.' |
1541 ]. |
1569 ]. |
1542 |
1570 |
1543 ((string startsWith:'[') or:[string endsWith:']']) ifTrue:[ |
1571 ((string startsWith:'[') or:[string endsWith:']']) ifTrue:[ |
1544 shortText ifTrue:[ |
1572 shortText ifTrue:[ |
1545 ^ '"[..]" - a block (aka lambda/closure for experts)'. |
1573 ^ '"[..]" - a ',(self asClassLink:'Block'),' (aka lambda/closure for experts)'. |
1546 ]. |
1574 ]. |
1547 ^ '[:arg1 .. :argN | statements] |
1575 ^ '[:arg1 .. :argN | statements] |
1548 |
1576 |
1549 defines a block. |
1577 defines a block. |
1550 Blocks represent pieces of executable code. The definition of a block does |
1578 Blocks represent pieces of executable code. The definition of a block does |
1556 Blocks are also often used as callbacks from UI components or as exception handlers.' |
1584 Blocks are also often used as callbacks from UI components or as exception handlers.' |
1557 ]. |
1585 ]. |
1558 |
1586 |
1559 ((string startsWith:'{') or:[string endsWith:'}']) ifTrue:[ |
1587 ((string startsWith:'{') or:[string endsWith:'}']) ifTrue:[ |
1560 shortText ifTrue:[ |
1588 shortText ifTrue:[ |
1561 ^ '"{..}" array instantiation (syntax)'. |
1589 ^ '"{..}" ',(self asClassLink:'Array'),' instantiation (syntax)'. |
1562 ]. |
1590 ]. |
1563 ^ '{ <expr1>. .. <exprN> } |
1591 ^ '{ <expr1>. .. <exprN> } |
1564 |
1592 |
1565 This is syntactic sugar for "Array with:<expr1> .. with:<exprN>". |
1593 This is syntactic sugar for "Array with:<expr1> .. with:<exprN>". |
1566 |
1594 |
1660 ! |
1688 ! |
1661 |
1689 |
1662 typeDescriptionFor:setOfTypes andSelector:selectorOrNil |
1690 typeDescriptionFor:setOfTypes andSelector:selectorOrNil |
1663 "up to 3 types are shown by name; more are simply counted" |
1691 "up to 3 types are shown by name; more are simply counted" |
1664 |
1692 |
1693 ^ self typeDescriptionFor:setOfTypes andSelector:selectorOrNil wordBetween:'or' |
|
1694 ! |
|
1695 |
|
1696 typeDescriptionFor:setOfTypes andSelector:selectorOrNil wordBetween:wordbetween |
|
1697 "up to 3 types are shown by name; more are simply counted" |
|
1698 |
|
1665 |types numTypes |
1699 |types numTypes |
1666 type1 type2 type3 |
1700 type1 type2 type3 |
1667 nm1 nm2 nm3| |
1701 nm1 nm2 nm3 link| |
1668 |
1702 |
1669 "/ reduce... |
1703 "/ reduce... |
1670 self compressSetOfTypes:setOfTypes. |
1704 self compressSetOfTypes:setOfTypes. |
1671 setOfTypes isEmpty ifTrue:[^ nil]. |
1705 setOfTypes isEmpty ifTrue:[^ nil]. |
1672 |
1706 |
1674 types sortBySelector:#name. |
1708 types sortBySelector:#name. |
1675 |
1709 |
1676 "/ now make this a nice string |
1710 "/ now make this a nice string |
1677 numTypes := types size. |
1711 numTypes := types size. |
1678 type1 := types first. |
1712 type1 := types first. |
1679 nm1 := type1 name actionForAll:(self actionToBrowseClass:type1 selector:selectorOrNil). |
1713 nm1 := self asLink:type1 name to:(self actionToBrowseClass:type1 selector:selectorOrNil). |
1680 numTypes == 1 ifTrue:[ |
1714 numTypes == 1 ifTrue:[ |
1681 ^ nm1 |
1715 ^ nm1 |
1682 ]. |
1716 ]. |
1717 |
|
1683 type2 := types second. |
1718 type2 := types second. |
1684 nm2 := type2 name actionForAll:(self actionToBrowseClass:type2 selector:selectorOrNil). |
1719 nm2 := self asLink:type2 name to:(self actionToBrowseClass:type2 selector:selectorOrNil). |
1685 numTypes == 2 ifTrue:[ |
1720 numTypes == 2 ifTrue:[ |
1686 ^ nm1,' or ',nm2 |
1721 ^ nm1,' ',wordbetween,' ',nm2 |
1687 ]. |
1722 ]. |
1688 type3 := types third. |
1723 type3 := types third. |
1689 nm3 := type3 name actionForAll:(self actionToBrowseClass:type3 selector:selectorOrNil). |
1724 nm3 := self asLink:type3 name to:(self actionToBrowseClass:type3 selector:selectorOrNil). |
1690 numTypes == 3 ifTrue:[ |
1725 numTypes == 3 ifTrue:[ |
1691 ^ nm1,', ',nm2,' or ',nm3 |
1726 ^ nm1,', ',nm2,' ',wordbetween,' ',nm3 |
1692 ]. |
1727 ]. |
1693 ^ ('one of %1 classes' bindWith:numTypes) actionForAll:(self actionToBrowseClasses:types). |
1728 link := self actionToBrowseClasses:types. |
1729 "/ selectorOrNil notNil ifTrue:[ |
|
1730 "/ link := self actionToOpenMethodFinderFor:selectorOrNil. |
|
1731 "/ ]. |
|
1732 ^ self asLink:('%1 classes' bindWith:numTypes) to:link. |
|
1694 ! |
1733 ! |
1695 |
1734 |
1696 valueStringFor:aValue |
1735 valueStringFor:aValue |
1697 |valString| |
1736 |valString| |
1698 |
1737 |
1728 ! ! |
1767 ! ! |
1729 |
1768 |
1730 !Explainer class methodsFor:'naive type inferer'! |
1769 !Explainer class methodsFor:'naive type inferer'! |
1731 |
1770 |
1732 addTypeOfExpressionNode:expr forAssignmentTo:varName to:setOfTypes |
1771 addTypeOfExpressionNode:expr forAssignmentTo:varName to:setOfTypes |
1733 |val valClass msgSelector msgReceiver| |
1772 "pick up low hanging type information. |
1773 This is far from being complete, but often gives a hint good enough for code completion |
|
1774 and info in the browser." |
|
1775 |
|
1776 |val valClass| |
|
1734 |
1777 |
1735 "/ only look for wellknown types on the right side. |
1778 "/ only look for wellknown types on the right side. |
1736 expr isLiteral ifTrue:[ |
1779 expr isLiteral ifTrue:[ |
1737 val := expr value. |
1780 val := expr value. |
1738 valClass := val class. |
1781 valClass := val class. |
1742 self rememberType:valClass in:setOfTypes. |
1785 self rememberType:valClass in:setOfTypes. |
1743 ^ setOfTypes. |
1786 ^ setOfTypes. |
1744 ]. |
1787 ]. |
1745 |
1788 |
1746 expr isMessage ifTrue:[ |
1789 expr isMessage ifTrue:[ |
1747 msgSelector := expr selector. |
1790 self addTypeOfMessageNode:expr forAssignmentTo:varName to:setOfTypes. |
1748 msgReceiver := expr receiver. |
1791 ^ setOfTypes. |
1749 |
1792 ]. |
1750 msgSelector == #? ifTrue:[ |
1793 |
1751 self addTypeOfExpressionNode:(expr arg1) forAssignmentTo:varName to:setOfTypes. |
|
1752 ^ setOfTypes |
|
1753 ]. |
|
1754 "/ really really only low hanging fruit... |
|
1755 "/ ignore / here, because of filename |
|
1756 ( #(+ - *) includes:msgSelector ) ifTrue:[ |
|
1757 "/ ignore foo := foo OP expr |
|
1758 "/ ignore foo := expr OP foo |
|
1759 (msgReceiver isVariable and:[msgReceiver name = varName]) ifFalse:[ |
|
1760 (expr arg1 isVariable and:[expr arg1 name = varName]) ifFalse:[ |
|
1761 self rememberType:Number in:setOfTypes. |
|
1762 ] |
|
1763 ]. |
|
1764 ^ setOfTypes. |
|
1765 ]. |
|
1766 |
|
1767 ( #(// size hash identityHash) includes:msgSelector ) ifTrue:[ |
|
1768 self rememberType:Integer in:setOfTypes. |
|
1769 ^ setOfTypes. |
|
1770 ]. |
|
1771 ( #(next next:) includes:msgSelector ) ifTrue:[ |
|
1772 |rcvrTypes| |
|
1773 |
|
1774 rcvrTypes := self addTypeOfExpressionNode:msgReceiver forAssignmentTo:nil to:Set new. |
|
1775 rcvrTypes notEmpty ifTrue:[ |
|
1776 self halt. |
|
1777 self rememberType:Character in:setOfTypes. |
|
1778 ]. |
|
1779 ^ setOfTypes. |
|
1780 ]. |
|
1781 ( msgSelector startsWith:'as') ifTrue:[ |
|
1782 valClass := Smalltalk classNamed:(msgSelector copyFrom:3). |
|
1783 valClass notNil ifTrue:[ |
|
1784 self rememberType:valClass in:setOfTypes. |
|
1785 ^ setOfTypes. |
|
1786 ]. |
|
1787 ]. |
|
1788 ( #(copy shallowCopy) includes:msgSelector ) ifTrue:[ |
|
1789 msgReceiver isLiteral ifTrue:[ |
|
1790 self addTypeOfExpressionNode:msgReceiver forAssignmentTo:varName to:setOfTypes. |
|
1791 ]. |
|
1792 ^ setOfTypes |
|
1793 ]. |
|
1794 |
|
1795 msgReceiver isGlobal ifTrue:[ |
|
1796 |globalValue| |
|
1797 |
|
1798 globalValue := msgReceiver value. |
|
1799 globalValue isBehavior ifTrue:[ |
|
1800 ( #(new new: basicNew basicNew:) includes:msgSelector ) ifTrue:[ |
|
1801 self rememberType:globalValue in:setOfTypes. |
|
1802 ^ setOfTypes. |
|
1803 ]. |
|
1804 ]. |
|
1805 self breakPoint:#cg. |
|
1806 ] ifFalse:[ |
|
1807 self breakPoint:#cg. |
|
1808 ] |
|
1809 ]. |
|
1810 ^ setOfTypes |
1794 ^ setOfTypes |
1811 |
1795 |
1812 "Created: / 30-04-2016 / 15:28:59 / cg" |
1796 "Created: / 30-04-2016 / 15:28:59 / cg" |
1813 "Modified: / 30-04-2016 / 20:17:35 / cg" |
1797 "Modified: / 30-04-2016 / 20:17:35 / cg" |
1814 ! |
1798 ! |
1815 |
1799 |
1800 addTypeOfMessageNode:expr forAssignmentTo:varName to:setOfTypes |
|
1801 "pick up low hanging type information. |
|
1802 This is far from being complete, but often gives a hint good enough for code completion |
|
1803 and info in the browser." |
|
1804 |
|
1805 |valClass msgSelector msgReceiver| |
|
1806 |
|
1807 msgSelector := expr selector. |
|
1808 msgReceiver := expr receiver. |
|
1809 |
|
1810 msgSelector == #? ifTrue:[ |
|
1811 self addTypeOfExpressionNode:(expr arg1) forAssignmentTo:varName to:setOfTypes. |
|
1812 ^ setOfTypes |
|
1813 ]. |
|
1814 |
|
1815 "/ really really only very low hanging fruit... |
|
1816 "/ ignore #/ here, because of filename |
|
1817 ( #(+ - *) includes:msgSelector ) ifTrue:[ |
|
1818 "/ ignore foo := foo OP expr |
|
1819 "/ ignore foo := expr OP foo |
|
1820 (msgReceiver isVariable and:[msgReceiver name = varName]) ifFalse:[ |
|
1821 (expr arg1 isVariable and:[expr arg1 name = varName]) ifFalse:[ |
|
1822 self rememberType:Number in:setOfTypes. |
|
1823 ] |
|
1824 ]. |
|
1825 ^ setOfTypes. |
|
1826 ]. |
|
1827 |
|
1828 ( #(// size hash identityHash) includes:msgSelector ) ifTrue:[ |
|
1829 self rememberType:Integer in:setOfTypes. |
|
1830 ^ setOfTypes. |
|
1831 ]. |
|
1832 |
|
1833 ( #(next next:) includes:msgSelector ) ifTrue:[ |
|
1834 |rcvrTypes| |
|
1835 |
|
1836 rcvrTypes := self addTypeOfExpressionNode:msgReceiver forAssignmentTo:nil to:Set new. |
|
1837 rcvrTypes notEmpty ifTrue:[ |
|
1838 self breakPoint:#cg. |
|
1839 self rememberType:Character in:setOfTypes. |
|
1840 ]. |
|
1841 ^ setOfTypes. |
|
1842 ]. |
|
1843 |
|
1844 ( msgSelector startsWith:'as') ifTrue:[ |
|
1845 valClass := Smalltalk classNamed:(msgSelector copyFrom:3). |
|
1846 valClass notNil ifTrue:[ |
|
1847 self rememberType:valClass in:setOfTypes. |
|
1848 ^ setOfTypes. |
|
1849 ]. |
|
1850 ]. |
|
1851 |
|
1852 ( #(copy shallowCopy) includes:msgSelector ) ifTrue:[ |
|
1853 msgReceiver isLiteral ifTrue:[ |
|
1854 self addTypeOfExpressionNode:msgReceiver forAssignmentTo:varName to:setOfTypes. |
|
1855 ]. |
|
1856 ^ setOfTypes |
|
1857 ]. |
|
1858 |
|
1859 msgReceiver isGlobal ifTrue:[ |
|
1860 |instCreatorMessages globalValue implMethod| |
|
1861 |
|
1862 instCreatorMessages := #(new new: basicNew basicNew:). |
|
1863 |
|
1864 globalValue := msgReceiver value. |
|
1865 globalValue isBehavior ifTrue:[ |
|
1866 ( instCreatorMessages includes:msgSelector ) ifTrue:[ |
|
1867 self rememberType:globalValue in:setOfTypes. |
|
1868 ^ setOfTypes. |
|
1869 ]. |
|
1870 implMethod := globalValue class lookupMethodFor:msgSelector. |
|
1871 "/ mhmh - fuzzy; if the implementing message sends any of the above to itself... |
|
1872 "/ assume it is returning it. |
|
1873 implMethod isNil ifTrue:[ |
|
1874 "/ will not be understood |
|
1875 self breakPoint:#cg. |
|
1876 ^ setOfTypes. |
|
1877 ]. |
|
1878 (implMethod messagesSentToSelf includesAny:instCreatorMessages) ifTrue:[ |
|
1879 self breakPoint:#cg. |
|
1880 self rememberType:globalValue in:setOfTypes. |
|
1881 ^ setOfTypes. |
|
1882 ]. |
|
1883 "/ very fuzzy - if the implementing method is in the "instance creation" category... |
|
1884 ((implMethod category ? '') startsWith:'instance creation') ifTrue:[ |
|
1885 self breakPoint:#cg. |
|
1886 self rememberType:globalValue in:setOfTypes. |
|
1887 ^ setOfTypes. |
|
1888 ]. |
|
1889 ]. |
|
1890 self breakPoint:#cg. |
|
1891 ^ setOfTypes |
|
1892 ]. |
|
1893 |
|
1894 self breakPoint:#cg. |
|
1895 ^ setOfTypes |
|
1896 ! |
|
1897 |
|
1816 addTypesAssignedToInstvar:instVarName inClass:aClass method:aMethod to:setOfTypes |
1898 addTypesAssignedToInstvar:instVarName inClass:aClass method:aMethod to:setOfTypes |
1817 "look to asssignments to an instance variable, and pick up low hanging class information. |
1899 "look to asssignments to an instance variable, and pick up low hanging class information. |
1818 This is far from being complete, but gives a hint good enough for code completion |
1900 This is far from being complete, but often gives a hint good enough for code completion |
1819 and info in the browser." |
1901 and info in the browser." |
1820 |
1902 |
1821 | code | |
1903 | code | |
1822 |
1904 |
1823 code := aMethod source. |
1905 code := aMethod source. |
1828 "Created: / 30-04-2016 / 15:07:33 / cg" |
1910 "Created: / 30-04-2016 / 15:07:33 / cg" |
1829 ! |
1911 ! |
1830 |
1912 |
1831 addTypesAssignedToInstvar:instVarName inClass:aClass source:code to:setOfTypes |
1913 addTypesAssignedToInstvar:instVarName inClass:aClass source:code to:setOfTypes |
1832 "look to asssignments to an instance variable, and pick up low hanging class information. |
1914 "look to asssignments to an instance variable, and pick up low hanging class information. |
1833 This is far from being complete, but gives a hint good enough for code completion |
1915 This is far from being complete, but often gives a hint good enough for code completion |
1834 and info in the browser." |
1916 and info in the browser." |
1835 |
1917 |
1836 |tree| |
1918 |tree| |
1837 |
1919 |
1838 "/ quick check (avoids expensive parse) |
1920 "/ quick check (avoids expensive parse) |
1846 "Created: / 30-04-2016 / 15:09:18 / cg" |
1928 "Created: / 30-04-2016 / 15:09:18 / cg" |
1847 ! |
1929 ! |
1848 |
1930 |
1849 addTypesAssignedToInstvar:instVarName inClass:aClass to:setOfTypes |
1931 addTypesAssignedToInstvar:instVarName inClass:aClass to:setOfTypes |
1850 "look to asssignments to an instance variable, and pick up low hanging class information. |
1932 "look to asssignments to an instance variable, and pick up low hanging class information. |
1851 This is far from being complete, but gives a hint good enough for code completion |
1933 This is far from being complete, but often gives a hint good enough for code completion |
1852 and info in the browser." |
1934 and info in the browser." |
1853 |
1935 |
1854 aClass methodDictionary do:[:m | |
1936 aClass methodDictionary do:[:m | |
1855 self addTypesAssignedToInstvar:instVarName inClass:aClass method:m to:setOfTypes |
1937 self addTypesAssignedToInstvar:instVarName inClass:aClass method:m to:setOfTypes |
1856 ]. |
1938 ]. |
1858 "Created: / 30-04-2016 / 14:52:56 / cg" |
1940 "Created: / 30-04-2016 / 14:52:56 / cg" |
1859 ! |
1941 ! |
1860 |
1942 |
1861 addTypesAssignedToInstvar:instVarName inTree:tree to:setOfTypes |
1943 addTypesAssignedToInstvar:instVarName inTree:tree to:setOfTypes |
1862 "look to asssignments to an instance variable, and pick up low hanging class information. |
1944 "look to asssignments to an instance variable, and pick up low hanging class information. |
1863 This is far from being complete, but gives a hint good enough for code completion |
1945 This is far from being complete, but often gives a hint good enough for code completion |
1864 and info in the browser." |
1946 and info in the browser." |
1865 |
1947 |
1866 |visitor| |
1948 |visitor| |
1867 |
1949 |
1868 visitor := PluggableParseNodeVisitor new. |
1950 visitor := PluggableParseNodeVisitor new. |
1869 visitor |
1951 visitor |
1870 actionForNodeClass:AssignmentNode |
1952 actionForNodeClass:AssignmentNode |
1871 put:[:node | |
1953 put:[:node | |
1872 |leftSide expr| |
1954 (node variable isInstanceVariableNamed:instVarName) ifTrue:[ |
1873 |
1955 self addTypeOfExpressionNode:(node expression) forAssignmentTo:instVarName to:setOfTypes |
1874 leftSide := node variable. |
|
1875 (leftSide isInstanceVariable and:[ leftSide name = instVarName ]) ifTrue:[ |
|
1876 expr := node expression. |
|
1877 self addTypeOfExpressionNode:expr forAssignmentTo:instVarName to:setOfTypes |
|
1878 ]. |
1956 ]. |
1879 true "/ yes - visit subnodes |
1957 true "/ yes - visit subnodes |
1880 ]. |
1958 ]. |
1881 visitor visit:tree. |
1959 visitor visit:tree. |
1882 ! |
1960 ! |
1883 |
1961 |
1884 addTypesAssignedToLocal:localName inTree:tree to:setOfTypes |
1962 addTypesAssignedToLocal:localName inTree:tree to:setOfTypes |
1885 "look to asssignments to an instance variable, and pick up low hanging class information. |
1963 "look to asssignments to a local variable, and pick up low hanging class information. |
1886 This is far from being complete, but gives a hint good enough for code completion |
1964 This is far from being complete, but often gives a hint good enough for code completion |
1887 and info in the browser." |
1965 and info in the browser." |
1888 |
1966 |
1889 |visitor| |
1967 |visitor| |
1890 |
1968 |
1891 "/ hack, allowing to deal with both types of AST (sigh) |
1969 "/ hack, allowing to deal with both types of AST (sigh) |
1892 (tree isKindOf:(Smalltalk at:#RBProgramNode)) ifTrue:[ |
1970 (tree isKindOf:(Smalltalk at:#RBProgramNode)) ifTrue:[ |
1893 visitor := (Smalltalk at:#RBPluggableProgramNodeVisitor) new. |
1971 visitor := (Smalltalk at:#RBPluggableProgramNodeVisitor) new. |
1894 visitor |
1972 visitor |
1895 actionForNodeClass:(Smalltalk at:#RBAssignmentNode) |
1973 actionForNodeClass:(Smalltalk at:#RBAssignmentNode) |
1896 put:[:node | |
1974 put:[:node | |
1897 |leftSide expr| |
1975 |leftSide| |
1898 |
1976 |
1899 leftSide := node variable. |
1977 leftSide := node variable. |
1900 (leftSide isTemporary and:[ leftSide name = localName ]) ifTrue:[ |
1978 (leftSide isTemporary and:[ leftSide name = localName ]) ifTrue:[ |
1901 expr := node value. |
1979 self addTypeOfExpressionNode:(node value) forAssignmentTo:localName to:setOfTypes |
1902 self addTypeOfExpressionNode:expr forAssignmentTo:localName to:setOfTypes |
|
1903 ]. |
1980 ]. |
1904 true "/ yes - visit subnodes |
1981 true "/ yes - visit subnodes |
1905 ]. |
1982 ]. |
1906 visitor visitNode:tree. |
1983 visitor visitNode:tree. |
1907 ] ifFalse:[ |
1984 ] ifFalse:[ |
1996 "Modified: / 30-04-2016 / 20:05:09 / cg" |
2073 "Modified: / 30-04-2016 / 20:05:09 / cg" |
1997 ! ! |
2074 ! ! |
1998 |
2075 |
1999 !Explainer class methodsFor:'utilities'! |
2076 !Explainer class methodsFor:'utilities'! |
2000 |
2077 |
2078 actionToBrowseClass:class |
|
2079 ^ self actionToBrowseClass:class selector:nil. |
|
2080 ! |
|
2081 |
|
2001 actionToBrowseClass:class selector:selectorOrNil |
2082 actionToBrowseClass:class selector:selectorOrNil |
2002 selectorOrNil isNil ifTrue:[ |
2083 ^ [ |
2003 ^ [Tools::NewSystemBrowser openInClass:class] |
2084 self thisOrNewBrowserInto:[:browser :openHow | |
2004 ] ifFalse:[ |
2085 browser |
2005 ^ [Tools::NewSystemBrowser openInClass:class selector:selectorOrNil] |
2086 spawnFullBrowserInClass:class selector:selectorOrNil in:openHow |
2006 ]. |
2087 "/ spawnMethodBrowserFor:{class compiledMethodAt:selectorOrNil} |
2088 "/ in:openHow |
|
2089 "/ label:nil |
|
2090 ] |
|
2091 ]. |
|
2007 ! |
2092 ! |
2008 |
2093 |
2009 actionToBrowseClasses:classes |
2094 actionToBrowseClasses:classes |
2010 ^ [Tools::NewSystemBrowser browseClasses:classes] |
2095 ^ [ |
2011 ! |
2096 self thisOrNewBrowserInto:[:browser :openHow | |
2012 |
2097 browser |
2013 actionToBrowseImplementorsOf:selector |
2098 spawnClassBrowserFor:classes in:openHow |
2099 ] |
|
2100 ] |
|
2101 "/ ^ [Tools::NewSystemBrowser browseClasses:classes] |
|
2102 ! |
|
2103 |
|
2104 actionToBrowseImplementorsOf:selector |
|
2105 ^ [ |
|
2106 self thisOrNewBrowserInto:[:browser :openHow | |
|
2107 browser |
|
2108 spawnMethodImplementorsBrowserFor:{ selector } |
|
2109 in:openHow |
|
2110 ] |
|
2111 ] |
|
2112 ! |
|
2113 |
|
2114 actionToBrowseInstvarRefsTo:instVarName inClass:class |
|
2115 ^ [(Tools::NewSystemBrowser basicNew) |
|
2116 browseVarRefsToAny:{ instVarName } |
|
2117 classes:{ class } |
|
2118 variables:#instVarNames access:#readOrWrite all:true |
|
2119 title:'references to ',instVarName |
|
2120 in:#newBrowser |
|
2121 ] |
|
2122 ! |
|
2123 |
|
2124 actionToBrowseMethod:mthd |
|
2125 ^ self actionToBrowseClass:(mthd mclass) selector:(mthd selector). |
|
2126 ! |
|
2127 |
|
2128 actionToOpenMethodFinderFor:selector |
|
2014 MethodFinderWindow notNil ifTrue:[ |
2129 MethodFinderWindow notNil ifTrue:[ |
2015 ^ [MethodFinderWindow openOnSelectorPattern:selector]. |
2130 ^ [MethodFinderWindow openOnSelectorPattern:selector]. |
2016 ]. |
2131 ]. |
2132 ^ self actionToBrowseImplementorsOf:selector |
|
2133 ! |
|
2134 |
|
2135 asClassLink:nameOfClass |
|
2136 "return text with a hyperlink to browse a class by that name" |
|
2017 |
2137 |
2018 ^ [UserPreferences systemBrowserClass browseImplementorsOf:selector] |
2138 |cls| |
2139 |
|
2140 cls := Smalltalk classNamed:nameOfClass string asUppercaseFirst. |
|
2141 cls isNil ifTrue:[^ nameOfClass]. |
|
2142 |
|
2143 ^ self asLink:nameOfClass to:(self actionToBrowseClass:cls) |
|
2144 ! |
|
2145 |
|
2146 asLink:aString to:action |
|
2147 ^ (aString actionForAll:action) |
|
2148 withColor:(Color blue) |
|
2149 ! |
|
2150 |
|
2151 infoStringForClasses:aCollectionOfClasses withPrefix:prefix |
|
2152 "get a nice user readable list for some classes. |
|
2153 Up to 4 are named, otherwise the count is presented. |
|
2154 The prefix can be sth like ' other', ' sub', ' super', |
|
2155 ' implementing' etc. Or it can be an empty string. |
|
2156 To be shown in the info line at the bottom." |
|
2157 |
|
2158 |nClassNames classes sortedByName classNames |
|
2159 link1 link2 link3 link4| |
|
2160 |
|
2161 aCollectionOfClasses isEmpty ifTrue:[ |
|
2162 ^ 'No %1classes' bindWith:prefix. |
|
2163 ]. |
|
2164 |
|
2165 classes := aCollectionOfClasses asIdentitySet asOrderedCollection. |
|
2166 classNames := classes collect:[:each | each theNonMetaclass name]. |
|
2167 |
|
2168 nClassNames := classNames size. |
|
2169 |
|
2170 nClassNames <= 4 ifTrue:[ |
|
2171 sortedByName := classNames sortWith:classes. |
|
2172 |
|
2173 link1 := self asLink:classNames first "allBold" to:(self actionToBrowseClass:classes first). |
|
2174 nClassNames == 1 ifTrue:[ |
|
2175 ^ '%2' "'1 %1class: %2'" |
|
2176 bindWith:prefix |
|
2177 with:link1. |
|
2178 ]. |
|
2179 link2 := self asLink:classNames second "allBold" to:(self actionToBrowseClass:classes second). |
|
2180 nClassNames == 2 ifTrue:[ |
|
2181 ^ '%2 and %3' "'2 %1classes: %2 and %3'" |
|
2182 bindWith:prefix |
|
2183 with:link1 |
|
2184 with:link2. |
|
2185 ]. |
|
2186 link3 := self asLink:classNames third "allBold" to:(self actionToBrowseClass:classes third). |
|
2187 nClassNames == 3 ifTrue:[ |
|
2188 ^ '%2, %3 and %4' "'3 %1classes: %2, %3 and %4'" |
|
2189 bindWith:prefix |
|
2190 with:link1 |
|
2191 with:link2 |
|
2192 with:link3. |
|
2193 ]. |
|
2194 link4 := self asLink:classNames fourth "allBold" to:(self actionToBrowseClass:classes fourth). |
|
2195 nClassNames == 4 ifTrue:[ |
|
2196 ^ '%2, %3, %4 and %5' "'4 %1classes: %2, %3, %4 and %5'" |
|
2197 bindWith:prefix |
|
2198 with:link1 |
|
2199 with:link2 |
|
2200 with:link3 |
|
2201 with:link4. |
|
2202 ]. |
|
2203 ]. |
|
2204 ^ self asLink:('%1 %2classes' bindWith:(nClassNames printString "allBold") with:prefix) |
|
2205 to:(self actionToBrowseClasses:classes) |
|
2206 |
|
2207 "Modified: / 27-07-2006 / 10:09:02 / cg" |
|
2208 ! |
|
2209 |
|
2210 infoStringForMethods:aCollectionOfMethods withPrefix:prefix |
|
2211 "get a nice user readable list for some methods. |
|
2212 Up to 3 are named, otherwise the count is presented. |
|
2213 The prefix can be sth like ' other', ' sender', ' implementor', |
|
2214 Or it can be an empty string. |
|
2215 Result is meant to be shown in the info line at the bottom of a browser." |
|
2216 |
|
2217 |nMethodNames sortedByName methodNames| |
|
2218 |
|
2219 aCollectionOfMethods isEmpty ifTrue:[ |
|
2220 ^ 'No %1' bindWith:prefix. |
|
2221 ]. |
|
2222 |
|
2223 methodNames := aCollectionOfMethods asOrderedCollection |
|
2224 collect:[:each | each whoString]. |
|
2225 |
|
2226 nMethodNames := methodNames size. |
|
2227 |
|
2228 nMethodNames <= 3 ifTrue:[ |
|
2229 nMethodNames == 1 ifTrue:[ |
|
2230 ^ '%2' "'1 %1class: %2'" bindWith:prefix with:(methodNames first allBold). |
|
2231 ]. |
|
2232 sortedByName := methodNames sort. |
|
2233 nMethodNames == 2 ifTrue:[ |
|
2234 ^ '%2 and %3' "'2 %1classes: %2 and %3'" bindWith:prefix |
|
2235 with:(sortedByName first allBold) |
|
2236 with:(sortedByName second allBold). |
|
2237 ]. |
|
2238 nMethodNames == 3 ifTrue:[ |
|
2239 ^ '%2, %3 and %4' "'3 %1classes: %2, %3 and %4'" bindWith:prefix |
|
2240 with:(sortedByName first allBold) |
|
2241 with:(sortedByName second allBold) |
|
2242 with:(sortedByName third allBold). |
|
2243 ]. |
|
2244 nMethodNames == 4 ifTrue:[ |
|
2245 ^ '%2, %3, %4 and %5' "'4 %1classes: %2, %3, %4 and %5'" bindWith:prefix |
|
2246 with:(sortedByName first allBold) |
|
2247 with:(sortedByName second allBold) |
|
2248 with:(sortedByName third allBold) |
|
2249 with:(sortedByName fourth allBold). |
|
2250 ]. |
|
2251 ]. |
|
2252 ^ '%1 %2methods' bindWith:nMethodNames printString allBold with:prefix. |
|
2253 |
|
2254 " |
|
2255 Time millisecondsToRun:[ |
|
2256 self infoStringForMethods:(SystemBrowser allCallsOn:#'at:put:') withPrefix:'' |
|
2257 ]. |
|
2258 Time millisecondsToRun:[ |
|
2259 self infoStringForMethods:(SystemBrowser allCallsOn:#'actionToBrowseClasses:') withPrefix:'' |
|
2260 ]. |
|
2261 " |
|
2262 ! |
|
2263 |
|
2264 methodImplementorsInfoFor:aMethod inEnvironment:environment |
|
2265 "get something about the implementors of aMethod |
|
2266 to be shown in the info line at the bottom" |
|
2267 |
|
2268 |implementors msg senders msg2| |
|
2269 |
|
2270 implementors := SystemBrowser |
|
2271 findImplementorsOf:aMethod selector |
|
2272 in:(environment allClasses) |
|
2273 ignoreCase:false. |
|
2274 |
|
2275 implementors notEmpty ifTrue:[ |
|
2276 msg := 'Only implemented here.'. |
|
2277 implementors remove:aMethod ifAbsent:nil. |
|
2278 implementors notEmpty ifTrue:[ |
|
2279 implementors := implementors collect:[:mthd | mthd mclass ? mthd getMclass]. |
|
2280 implementors notEmpty ifTrue:[ |
|
2281 msg := 'Also implemented in '. |
|
2282 msg := msg , (self infoStringForClasses:implementors withPrefix:'other '). |
|
2283 msg := msg , '.'. |
|
2284 ] |
|
2285 ]. |
|
2286 ]. |
|
2287 |
|
2288 false ifTrue:[ "/ too slow |
|
2289 senders := SystemBrowser |
|
2290 findSendersOf:aMethod selector |
|
2291 in:(environment allClasses) |
|
2292 ignoreCase:false. |
|
2293 senders notEmpty ifTrue:[ |
|
2294 msg2 := 'Sent from ' , senders size printString, ' methods.'. |
|
2295 ] ifFalse:[ |
|
2296 msg2 := 'No senders.'. |
|
2297 ]. |
|
2298 msg := msg , '/' , msg2 |
|
2299 ]. |
|
2300 |
|
2301 ^ msg |
|
2302 ! |
|
2303 |
|
2304 methodInheritanceInfoFor:aMethod |
|
2305 |methodsSuperclass inheritedClass msg methodsClass selector mthd selectorString| |
|
2306 |
|
2307 methodsClass := aMethod mclass. |
|
2308 methodsClass isNil ifTrue:[^ nil]. |
|
2309 |
|
2310 methodsSuperclass := methodsClass superclass. |
|
2311 methodsSuperclass isNil ifTrue:[^ nil]. |
|
2312 |
|
2313 selector := aMethod selector. |
|
2314 selector isNil ifTrue:[^ nil]. |
|
2315 |
|
2316 inheritedClass := methodsSuperclass whichClassIncludesSelector:selector. |
|
2317 inheritedClass isNil ifTrue:[^ nil]. |
|
2318 |
|
2319 mthd := inheritedClass compiledMethodAt:selector. |
|
2320 |
|
2321 (mthd sends:#'subclassResponsibility') ifTrue:[ |
|
2322 msg := '%1 overrides subclassResponsibility in %2'. |
|
2323 ] ifFalse:[ |
|
2324 msg := '%1 overrides implementation in %2'. |
|
2325 ]. |
|
2326 selectorString := selector contractTo:30. |
|
2327 ^ msg |
|
2328 bindWith:(self asLink:selectorString "allBold" to:(self actionToOpenMethodFinderFor:selector)) |
|
2329 with:(self asLink:inheritedClass name "allBold" |
|
2330 to:(self actionToBrowseClass:inheritedClass selector:selector)) |
|
2331 ! |
|
2332 |
|
2333 methodRedefinitionInfoFor:aMethod |
|
2334 "return a user readable string telling in how many subclasses |
|
2335 a method is redefined. |
|
2336 To be shown in the info line of a browser" |
|
2337 |
|
2338 |redefiningClasses msg cls| |
|
2339 |
|
2340 cls := aMethod mclass. |
|
2341 cls isNil ifTrue:[^ nil]. |
|
2342 |
|
2343 redefiningClasses := cls allSubclasses select:[:cls | cls includesSelector:aMethod selector. ]. |
|
2344 redefiningClasses size > 0 ifTrue:[ |
|
2345 msg := 'redefined in '. |
|
2346 msg := msg , (self infoStringForClasses:redefiningClasses withPrefix:'sub'). |
|
2347 msg := msg , '.'. |
|
2348 ]. |
|
2349 |
|
2350 ^ msg |
|
2351 ! |
|
2352 |
|
2353 methodSendersInfoFor:selector inEnvironment:environment |
|
2354 "get something about the senders of a message. |
|
2355 to be shown in the info line at the bottom. |
|
2356 This may be slow; so think about doing it in background..." |
|
2357 |
|
2358 |senders| |
|
2359 |
|
2360 senders := SystemBrowser |
|
2361 findSendersOf:selector |
|
2362 in:(environment allClasses) |
|
2363 ignoreCase:false |
|
2364 match:false. |
|
2365 |
|
2366 senders notEmpty ifTrue:[ |
|
2367 ^ 'Sent from ' , senders size printString, ' methods.'. |
|
2368 ] ifFalse:[ |
|
2369 ^ 'No senders.'. |
|
2370 ]. |
|
2371 ! |
|
2372 |
|
2373 methodSpecialInfoFor:aMethod |
|
2374 "handles special cases - such as documentation methods" |
|
2375 |
|
2376 |cls sel| |
|
2377 |
|
2378 (cls := aMethod mclass) isNil ifTrue:[^ nil]. |
|
2379 (sel := aMethod selector) isNil ifTrue:[^ nil]. |
|
2380 |
|
2381 cls isMeta ifTrue:[ |
|
2382 (AbstractSourceCodeManager isVersionMethodSelector:sel) ifTrue:[ |
|
2383 ^ 'The version method is required for the source code repository - do not modify.'. |
|
2384 ]. |
|
2385 sel == #documentation ifTrue:[ |
|
2386 ^ 'ST/X stores documentation in this method (not in comment slots)'. |
|
2387 ]. |
|
2388 ]. |
|
2389 ^ nil |
|
2390 ! |
|
2391 |
|
2392 thisOrNewBrowserInto:aTwoArgBlock |
|
2393 "if I am invoked by a browser, |
|
2394 invoke the twoArgBlock withit and an #newBuffer arg. |
|
2395 Otherwise, create a new (invisible) browser and pass it to the block |
|
2396 with a #newBrowser arg." |
|
2397 |
|
2398 |windowGroupClass browserClass wg app| |
|
2399 |
|
2400 "/ stupid: I am in libcomp; should be in libtool |
|
2401 windowGroupClass := Smalltalk at:#WindowGroup. |
|
2402 windowGroupClass isNil ifTrue:[^ self]. |
|
2403 browserClass := Smalltalk at:#'Tools::NewSystemBrowser'. |
|
2404 browserClass isNil ifTrue:[^ self]. |
|
2405 |
|
2406 ((wg := windowGroupClass activeGroup) notNil |
|
2407 and:[ (app := wg application) isKindOf:browserClass ] |
|
2408 ) ifTrue:[ |
|
2409 ^ aTwoArgBlock value:app value:#newBuffer |
|
2410 ]. |
|
2411 ^ aTwoArgBlock value:(browserClass basicNew) value:#newBrowser |
|
2019 ! ! |
2412 ! ! |
2020 |
2413 |
2021 !Explainer class methodsFor:'documentation'! |
2414 !Explainer class methodsFor:'documentation'! |
2022 |
2415 |
2023 version |
2416 version |