DoWhatIMeanSupport.st
changeset 5080 0842b08b7040
parent 5076 9d8cf635a6cb
child 5081 5478311e8ee5
--- a/DoWhatIMeanSupport.st	Sat May 07 12:30:34 2016 +0200
+++ b/DoWhatIMeanSupport.st	Sat May 07 12:37:59 2016 +0200
@@ -1508,7 +1508,7 @@
      If nonNil, we can make better guesses, because we actually know what a variable's type is"
 
     |crsrPos char interval i source partialSource cursorLineSource
-     suggestions actions title|
+     suggestions actions title suggestionCollector|
 
     languageOrNil := SmalltalkLanguage instance.
     methodOrNil := methodOrNilArg.
@@ -1527,13 +1527,36 @@
         crsrPos := crsrPos - 1.
         char := codeView characterAtCharacterPosition:crsrPos.
     ].
-
     char == $. ifTrue:[
         "/ either at end of statement or after a character constant
         crsrPos == 1 ifTrue:[^ self].
         (codeView characterAtCharacterPosition:crsrPos-1) == $$ ifFalse:[^ self].
     ].
 
+    suggestionCollector :=
+        [:listOfSuggestions :listOfActionsOrBlock :titleWhenAsking |
+            "/ may be called multiple times!!
+            suggestions isNil ifTrue:[
+                suggestions := listOfSuggestions.
+                actions := listOfActionsOrBlock.
+                title := titleWhenAsking.
+            ] ifFalse:[    
+                suggestions := suggestions asOrderedCollection.
+                actions isBlock ifTrue:[
+                    actions := Array new:suggestions size withAll:actions.
+                ].
+                actions := actions asOrderedCollection.
+                
+                suggestions addAll:listOfSuggestions.
+                listOfActionsOrBlock isBlock ifTrue:[
+                    actions addAll:(Array new:listOfSuggestions size withAll:listOfActionsOrBlock).
+                ] ifFalse:[
+                    actions addAll:listOfActionsOrBlock.
+                ].    
+                title := titleWhenAsking.
+            ].
+        ].
+        
     interval := crsrPos-1 to:crsrPos.
 
     source := codeView contentsAsString string.
@@ -1560,11 +1583,7 @@
             self
                 tryCodeCompletionWithSource:cursorLineSource nodeInterval:interval
                 at:crsrPos mustBeExpression:true
-                into:[:listOfSuggestions :listOfActions :titleWhenAsking |
-                    suggestions := listOfSuggestions.
-                    actions := listOfActions.
-                    title := titleWhenAsking.
-                ].
+                into:suggestionCollector.
         ].
     ].
     
@@ -1573,12 +1592,7 @@
 "/        self
 "/            tryCodeCompletionWithSource:partialSource nodeInterval:interval
 "/            at:crsrPos mustBeExpression:(classOrNilArg isNil and:[methodOrNilArg isNil])
-"/            into:[:listOfSuggestions :listOfActions :titleWhenAsking |
-"/                suggestions := listOfSuggestions.
-"/                actions := listOfActions.
-"/                title := titleWhenAsking.
-"/                "/ suggestions1 size>100 ifTrue:[ self halt].
-"/            ].
+"/            into:suggestionCollector.
 "/    ].
 
     suggestions isEmptyOrNil ifTrue:[
@@ -1586,11 +1600,7 @@
         self
             tryCodeCompletionWithSource:source nodeInterval:interval
             at:crsrPos mustBeExpression:false
-            into:[:listOfSuggestions :listOfActions :titleWhenAsking |
-                suggestions := listOfSuggestions.
-                actions := listOfActions.
-                title := titleWhenAsking.
-            ].
+            into:suggestionCollector
     ].
     
     suggestions isNil ifTrue:[
@@ -1598,12 +1608,7 @@
         self
             tryCodeCompletionWithSource:partialSource nodeInterval:interval
             at:crsrPos mustBeExpression:(classOrNilArg isNil and:[methodOrNilArg isNil])
-            into:[:listOfSuggestions :listOfActions :titleWhenAsking |
-                suggestions := listOfSuggestions.
-                actions := listOfActions.
-                title := titleWhenAsking.
-                "/ suggestions1 size>100 ifTrue:[ self halt].
-            ].
+            into:suggestionCollector.
     ].
     
     suggestions isEmptyOrNil ifTrue:[
@@ -2036,10 +2041,11 @@
         codeView characterBeforeCursor == $: ifTrue:[
             (bestSelectors select:[:sel | sel asLowercase startsWith:lcSelector]) isEmpty ifTrue:[
                 "/ nothing better around
-                |argIndex argNames impls|
+                |argIndex argNames argNameStrings impls|
  
                 argIndex := node selectorParts size.
                 argNames := Set new.
+                argNameStrings := OrderedCollection new.
                 impls := Smalltalk allImplementorsOf:selector.
                 impls size < 10 ifTrue:[
                     impls do:[:eachImplClass |
@@ -2048,12 +2054,15 @@
                         mthd := (eachImplClass compiledMethodAt:selector).
                         argName := (mthd methodArgNames ? #()) at:argIndex ifAbsent:nil.
                         argName notNil ifTrue:[
-                            argNames add:(argName,' in (' ,mthd mclass name allBold,' ',mthd methodDefinitionTemplate).
+                            (argNames includes:argName) ifFalse:[
+                                argNames add:argName.
+                                argNameStrings add:(argName allItalic,' hint only: argName in (' ,mthd mclass name allBold,' ',mthd methodDefinitionTemplate).
+                            ].    
                         ].
                     ].
-                    argNames notEmptyOrNil ifTrue: [
-                        argNames := argNames asOrderedCollection sort.
-                        actionBlock value:argNames value:[:selIndex | ] value: 'argument name hint'.
+                    argNameStrings notEmptyOrNil ifTrue: [
+                        argNameStrings := argNameStrings asOrderedCollection sort.
+                        actionBlock value:argNameStrings value:[:selIndex | ] value: 'argument name hint'.
                         ^ self.
                     ]
                 ]
@@ -2061,9 +2070,12 @@
         ].
     ] ifFalse:[
         "/ when completing a non-keyword AND the parent is a keyword message,
-        "/ only consider longer keyword messages or unary messages
-        (parentNode notNil and:[ parentNode isKeywordMessage ]) ifTrue:[
-            bestSelectors := bestSelectors select:[:sel | sel isUnarySelector ]
+        "/ only consider longer keyword messages or unary messages.
+        "/ unless the node is parenthesized
+        node hasParentheses ifFalse:[ 
+            (parentNode notNil and:[ parentNode isKeywordMessage ]) ifTrue:[
+                bestSelectors := bestSelectors select:[:sel | sel isUnarySelector or:[ sel startsWith:sel]]
+            ]
         ]
     ].
  
@@ -2588,7 +2600,7 @@
 codeCompletionForMessageTo:node into:actionBlock
     "find good suggestions for a message send to node, with no input yet"
     
-    |knownClass suggestions selectorsImplementedInClass mostUseful editAction pos|
+    |knownClass suggestions selectorsImplementedInClass mostUseful editActions pos|
  
     (knownClass := self classOfNode:node) isNil ifTrue:[^ self].
 
@@ -2645,8 +2657,10 @@
     self sortUsefulSelectorsIn:suggestions. "/cosmetics
 
     pos := codeView characterPositionOfCursor.
-    editAction := self editActionToReplaceCodeFrom:pos to:pos-1 byWordIn:suggestions.
-    actionBlock value:suggestions value:editAction value:nil.
+    editActions := suggestions collect:[:word |
+                        self editActionToReplaceCodeFrom:pos to:pos-1 by:word.
+                   ].                
+    actionBlock value:suggestions value:editActions value:nil.
 
     "Created: / 01-05-2016 / 17:01:21 / cg"
     "Modified: / 01-05-2016 / 18:54:03 / cg"
@@ -3043,7 +3057,7 @@
      char oldLen newLen
      getDistanceComputeBlockWithWeight addWithFactorBlock allTheBest bestAssoc
      globalFactor localFactor selectorOfMessageToNode implementors argIdx namesUsed kwPart
-     editAction suggestions nameIsOK longerNames setOfNames otherArgNames
+     editAction editActions suggestions nameIsOK longerNames setOfNames otherArgNames
      suggestionsWithInfo isLeftSideOfAssignment|
  
     "/ Transcript show:'var in '; show:methodOrNil; show:' / '; showCR:classOrNil.
@@ -3103,18 +3117,20 @@
                     ].
  
                 selectors := selectors1 order sort , #('-') , selectors2 order sort.
-                editAction :=
-                    [:answer |
-                        |s|
-                        s := answer isInteger ifTrue:[selectors at:answer] ifFalse:[answer].
-                        codeView
-                            undoableDo:[
-                                codeView insertString:s atCharacterPosition:crsrPos.
-                                codeView cursorToCharacterPosition:crsrPos+s size.
-                            ]
-                            info:'completion'.
-                    ].
-                actionBlock value:selectors value:editAction value:nil.
+                editActions := selectors collect:[:word | self editActionToInsert:word].
+                actionBlock value:selectors value:editActions value:nil.
+"/                editAction :=
+"/                    [:answer |
+"/                        |s|
+"/                        s := answer isInteger ifTrue:[selectors at:answer] ifFalse:[answer].
+"/                        codeView
+"/                            undoableDo:[
+"/                                codeView insertString:s atCharacterPosition:crsrPos.
+"/                                codeView cursorToCharacterPosition:crsrPos+s size.
+"/                            ]
+"/                            info:'completion'.
+"/                    ].
+"/                actionBlock value:selectors value:editAction value:nil.
                 ^ self.
             ].
         ].
@@ -3601,14 +3617,80 @@
                 ].
             ].
 
-    editAction := self editActionToReplaceNode:node byWordIn:suggestions.
-    actionBlock value:suggestionsWithInfo value:editAction value:nil.
+    editActions := suggestionsWithInfo collect:[:word |
+                     self editActionToReplaceNode:node by:word.
+                   ].        
+    actionBlock value:suggestionsWithInfo value:editActions value:nil.
 
     "Created: / 10-11-2006 / 13:16:33 / cg"
     "Modified: / 16-02-2010 / 10:13:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
     "Modified: / 01-05-2016 / 18:45:07 / cg"
 !
 
+editActionToInsert:aString
+    ^ [:index |
+        codeView
+            undoableDo:[
+                codeView insertSelectedStringAtCursor:aString.
+                codeView dontReplaceSelectionOnInput
+            ]
+            info:'Completion'.
+    ].
+!
+
+editActionToInsertFromSuggestions:suggestions
+    ^ [:index |
+        |answer|
+
+        answer := suggestions at:index.
+        codeView
+            undoableDo:[
+                codeView insertSelectedStringAtCursor:answer.
+                codeView dontReplaceSelectionOnInput
+            ]
+            info:'Completion'.
+    ].
+!
+
+editActionToReplaceCodeFrom:start to:stop by:aString
+    ^ [:index |
+        |oldVar oldLen newLen insertWithSpace|
+
+        insertWithSpace := false.
+
+        start <= stop ifTrue:[
+            oldVar := (codeView textFromCharacterPosition:start to:stop) asString string withoutSeparators.
+        ] ifFalse:[
+            codeView characterBeforeCursor == Character space ifTrue:[
+                insertWithSpace := true.
+            ].
+        ].
+
+        oldLen := stop - start + 1.
+        newLen := aString size.
+
+        codeView
+            undoableDo:[
+                insertWithSpace ifTrue:[
+                    codeView insertSelectedStringAtCursor:aString
+                ] ifFalse:[
+                    codeView replaceFromCharacterPosition:start to:stop with:aString.
+
+                    (aString startsWith:oldVar) ifTrue:[
+                        codeView selectFromCharacterPosition:start+oldLen to:start+newLen-1.
+                    ] ifFalse:[
+                        codeView selectFromCharacterPosition:start to:start+newLen-1.
+                    ].
+                ].
+                codeView dontReplaceSelectionOnInput
+            ]
+            info:'Completion'.
+
+    ].
+
+    "Created: / 01-05-2016 / 18:47:40 / cg"
+!
+
 editActionToReplaceCodeFrom:start to:stop byWordIn:suggestions
     ^ [:index |
         |answer oldVar oldLen newLen insertWithSpace|
@@ -3649,6 +3731,12 @@
     "Created: / 01-05-2016 / 18:47:40 / cg"
 !
 
+editActionToReplaceNode:node by:word
+    ^ self editActionToReplaceCodeFrom:node start to:node stop by:word
+
+    "Created: / 01-05-2016 / 18:44:09 / cg"
+!
+
 editActionToReplaceNode:node byWordIn:suggestions
     ^ self editActionToReplaceCodeFrom:node start to:node stop byWordIn:suggestions
 
@@ -3965,66 +4053,63 @@
     |optionalExtraSpace|
 
     optionalExtraSpace := (codeView characterAfterCursor isSeparator)
-			    ifTrue:['']
-			    ifFalse:[' '].
+                            ifTrue:['']
+                            ifFalse:[' '].
 
     (
-	#(
-	    'ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:'
-	    'and:' 'or:' 'timesRepeat:' 'whileTrue:' 'whileFalse:'
-	) includes:chosenCompletion
+        #(
+            'ifTrue:' 'ifFalse:' 'ifTrue:ifFalse:' 'ifFalse:ifTrue:'
+            'and:' 'or:' 'timesRepeat:' 'whileTrue:' 'whileFalse:'
+        ) includes:chosenCompletion
     ) ifTrue:[
-	codeView insertStringAtCursor:('[',optionalExtraSpace).
-	"/ codeView cursorLeft:1+extra size.
+        codeView insertStringAtCursor:('[',optionalExtraSpace,']').
+        "/ codeView cursorLeft:1+extra size.
     ].
     (
-	#(
-	    'collect:' 'select:' 'reject:' 'do:'
-	) includes:chosenCompletion
+        #(
+            'collect:' 'select:' 'reject:' 'do:'
+        ) includes:chosenCompletion
     ) ifTrue:[
-	codeView insertStringAtCursor:('[:each | ]',optionalExtraSpace).
-	codeView cursorLeft:1+optionalExtraSpace size.
+        codeView insertStringAtCursor:('[:each | ]',optionalExtraSpace).
+        codeView cursorLeft:1+optionalExtraSpace size.
     ].
     (
-	#(
-	    'contains:' 'findFirst:' 'detect:'
-	) includes:chosenCompletion
+        #(
+            'contains:' 'findFirst:' 'detect:'
+        ) includes:chosenCompletion
     ) ifTrue:[
-	codeView insertStringAtCursor:('[:some | ]',optionalExtraSpace).
-	codeView cursorLeft:1+optionalExtraSpace size.
+        codeView insertStringAtCursor:('[:some | ]',optionalExtraSpace).
+        codeView cursorLeft:1+optionalExtraSpace size.
     ].
     (
-	#(
-	    'remove:ifAbsent:' 'detect:ifNone:'
-	) includes:chosenCompletion
+        #(
+            'remove:ifAbsent:' 'detect:ifNone:'
+        ) includes:chosenCompletion
     ) ifTrue:[
-	codeView insertStringAtCursor:('[]',optionalExtraSpace).
-	codeView cursorLeft:1+optionalExtraSpace size.
+        codeView insertStringAtCursor:('[]',optionalExtraSpace).
+        codeView cursorLeft:1+optionalExtraSpace size.
     ].
 !
 
 messagesSentTo:varName in:aTree
-    |messagesToReceiver|
-    
-    "/ collect messages sent
-    messagesToReceiver := Set new.
-    "/ remembered nodes is nonNil if parser aborte with error
-    rememberedNodes notNil ifTrue:[
-        rememberedNodes do:[:node | 
+    |messagesToReceiver collector|
+
+    collector :=
+        [:node | 
             (node isMessage 
             and:[node receiver isVariable
             and:[node receiver name = varName]]) ifTrue:[
                 messagesToReceiver add:(node selector)
             ].
         ].
+        
+    "/ collect messages sent
+    messagesToReceiver := Set new.
+    "/ remembered nodes is nonNil if parser aborted with error
+    rememberedNodes notNil ifTrue:[
+        rememberedNodes do:collector.
     ] ifFalse:[
-        tree allMessageNodesDo:[:node |
-            (node isMessage 
-            and:[node receiver isVariable
-            and:[node receiver name = varName]]) ifTrue:[
-                messagesToReceiver add:node selector
-            ]
-        ].
+        tree allMessageNodesDo:collector.
     ]. 
     ^ messagesToReceiver
 !
@@ -4363,6 +4448,64 @@
         Transcript cr.
     ].
     
+    "/ if in a keyword-argument position...
+    node isMessage ifTrue:[
+        "/ where are we?
+        node selector isKeyword ifTrue:[
+            characterBeforeCursor == $: ifTrue:[
+                |argIdx senders implementors receiverClasses selectorUpToCursor implementorOfSelectorUpToCursor|
+                "/ about to enter an argument?
+                argIdx := node selectorParts keysAndValuesDetectKey:[:idx :part |
+                            part stop == (codeView characterPositionOfCursor-1). 
+                          ] ifNone:nil.
+                argIdx notNil ifTrue:[          
+                    selectorUpToCursor := ((node selectorParts collect:#value) copyTo:argIdx) asStringWith:''.        
+                    implementors := Set new.
+                    "/ find senders of this message, and see if they call it with a block argument
+                    "/ this takes too long for a completion;
+                    "/    Smalltalk allClassesDo:[:cls |
+                    "/        cls instAndClassMethodsDo:[:m |
+                    "/            (m sendsMessageForWhich:[:sel | sel startsWith:node selector]) ifTrue:[
+                    "/                senders add:m
+                    "/            ].    
+                    "/        ]
+                    "/    ].
+                    "/ therefore, restrict to a max. of 5 classes
+                    receiverClasses := self classesOfNode:node receiver.
+                    (receiverClasses notEmptyOrNil and:[receiverClasses size <= 5]) ifTrue:[
+                        receiverClasses do:[:eachPossibleReceiverClass |
+                            eachPossibleReceiverClass withAllSuperclassesDo:[:cls |
+                                cls methodDictionary keysAndValuesDo:[:sel :mthd |
+                                    (sel startsWith:selectorUpToCursor) ifTrue:[
+                                        implementors add:mthd.
+                                        (sel = selectorUpToCursor) ifTrue:[
+                                            implementorOfSelectorUpToCursor := implementorOfSelectorUpToCursor ? mthd
+                                        ].    
+                                    ].    
+                                ].    
+                            ]
+                        ]
+                    ].
+                    implementorOfSelectorUpToCursor notNil ifTrue:[
+                        |tree argName|
+                        
+                        tree := implementorOfSelectorUpToCursor parseTree.
+                        argName := tree argumentNames at:argIdx.
+                        (argName includesString:'block' caseSensitive:false) ifTrue:[
+                            actionBlock value:{'[ "',argName,'" ]'}
+                                        value:{ self editActionToInsert:('[ "',argName,'" ]') }
+                                        value:'block argument'
+                        ].
+                    ] ifFalse:[    
+                        implementors notEmpty ifTrue:[
+                            self halt.
+                        ].    
+                    ].
+                ].    
+            ].    
+        ].    
+    ].
+
     (node isVariable or:[node isBlock and:[node stop notNil]]) ifTrue:[
         (characterPositionOfCursor == (node stop + 1)
         "/ hack (spaces at end of line)
@@ -4515,8 +4658,50 @@
 
 !DoWhatIMeanSupport methodsFor:'code completion-helpers-naive type inference'!
 
+addClassesFromAssignmentTo:varName in:aTree to:setOfTypes
+    "/ assignments...
+    aTree allAssignmentNodesDo:[:eachAssignmentNode |
+        |exprCls leftSide|
+
+        leftSide := eachAssignmentNode variable.
+        leftSide name = varName ifTrue:[
+            exprCls := self classOfNode:eachAssignmentNode value.
+            exprCls notNil ifTrue:[ 
+                setOfTypes add:exprCls
+            ]
+        ]
+    ].
+    ^ setOfTypes.
+!
+
+addClassesOfBlockVar:variableNode inScope:blockScope to:setOfTypes
+    |blockParent|
+
+    blockParent := blockScope parent.
+    (blockParent notNil and:[blockParent isMessage]) ifFalse:[^ setOfTypes].
+    
+    "/ if the parent of the block is an enumeration message, and the receiver is known,
+    "/ we know the type of argument.
+    ( #(do: keysAndValuesDo: select: collect:) includes:blockParent selector) ifTrue:[
+        |collection|
+
+        collection := self valueOfNode:blockParent receiver.
+        collection notNil ifTrue:[
+            (collection isKindOf:Collection) ifTrue:[
+                collection notEmpty ifTrue:[
+                    |someElement|
+                    someElement := collection anElement.
+                    setOfTypes add:someElement class.
+                    ^ setOfTypes
+                ].
+            ].
+        ].
+    ].
+    ^ setOfTypes
+!
+
 addClassesOfExpression:expr inClass:classOrNil to:setOfTypes
-    |cls exprSelector exprVal varName instVarClass valClass
+    |cls exprSelector exprVal varName varScope instVarClass valClass
      msgSelector msgReceiver msgArg1
      receiverClasses receiverClass 
      arg1Classes mthd|
@@ -4527,47 +4712,59 @@
         (exprVal isArray or:[ exprVal isByteArray or:[ exprVal isString ]]) ifTrue:[
             exprVal isImmutable ifTrue:[
                 setOfTypes add:cls mutableClass.
-                ^ self.    
+                ^ setOfTypes.    
             ]
         ].
         setOfTypes add:cls. 
-        ^ self.    
+        ^ setOfTypes.    
     ].
     
     expr isBlock ifTrue:[
         setOfTypes add:Block. 
-        ^ self.
+        ^ setOfTypes.
     ].
     (exprVal := self valueOfNode:expr) notNil ifTrue:[
         "/ knowing the value is always great!!
         setOfTypes add:exprVal class.
-        ^ self.
+        ^ setOfTypes.
     ].
 
     expr isVariable ifTrue:[
         varName := expr name.
         varName = 'self' ifTrue:[
             setOfTypes add:(classOrNil ? UndefinedObject).
-            ^ self
+            ^ setOfTypes
         ].
         varName = 'super' ifTrue:[
             classOrNil isNil 
                 ifTrue:[setOfTypes add:Object]
                 ifFalse:[setOfTypes add:classOrNil superclass].
-            ^ self.    
+            ^ setOfTypes.    
         ].
         varName = 'thisContext' ifTrue:[
             setOfTypes add:Context.
-            ^ self
+            ^ setOfTypes
         ].
-
+        
+        varScope := expr whoDefines: varName.
+        (varScope notNil) ifTrue:[
+            (varScope isBlock) ifTrue:[
+                self addClassesOfBlockVar:expr inScope:varScope to:setOfTypes.
+                ^ setOfTypes
+            ].    
+            (varScope isMethod) ifTrue:[
+                setOfTypes addAll:( self classesFromAssignmentTo:varName in:varScope ).
+                ^ setOfTypes
+            ].    
+        ].
+        
         classOrNil notNil ifTrue:[
             instVarClass := classOrNil whichClassDefinesInstVar:varName.
             instVarClass notNil ifTrue:[
                 setOfTypes addAll:(self classesOfInstVarNamed:varName inClass:instVarClass).
             ].    
         ].
-        ^ self
+        ^ setOfTypes
     ].
 
     expr isMessage ifTrue:[
@@ -4586,7 +4783,7 @@
             ) includes:msgSelector
         ) ifTrue:[
             setOfTypes add:True. "/ use True, because boolean does not include the full protocol
-            ^ self    
+            ^ setOfTypes    
         ].
 
         msgReceiver := expr receiver.
@@ -4598,20 +4795,20 @@
         receiverClass notNil ifTrue:[
             ( #(copy shallowCopy) includes:exprSelector ) ifTrue:[
                 setOfTypes addAll:receiverClasses.
-                ^ self.
+                ^ setOfTypes.
             ].
 
             msgSelector == #theNonMetaclass ifTrue:[  
                 setOfTypes add:receiverClass theNonMetaclass class.
-                ^ self            
+                ^ setOfTypes            
             ].
             msgSelector == #theMetaclass ifTrue:[  
                 setOfTypes add:receiverClass theMetaclass class.
-                ^ self
+                ^ setOfTypes
             ].
             msgSelector == #class ifTrue:[
                 setOfTypes add:receiverClass class.
-                ^ self.
+                ^ setOfTypes.
             ].
 
             receiverClass isBehavior ifTrue:[
@@ -4619,20 +4816,20 @@
                 receiverClass isMeta ifTrue:[
                     ( #( #'new' #'basicNew' #'new:' #'basicNew:' #'with:' #'with:with:') includes: msgSelector ) ifTrue:[
                         setOfTypes add:receiverClass theNonMetaclass.
-                        ^ self.
+                        ^ setOfTypes.
                     ].
                     "/ if that method sends one of new/basicNew/new:/basicNew:, assume it returns an instance of itself
                     mthd notNil ifTrue:[
                         ( mthd sendsAny:#( #'new' #'basicNew' #'new:' #'basicNew:' )) ifTrue:[
                             setOfTypes add:receiverClass theNonMetaclass.
-                            ^ self
+                            ^ setOfTypes
                         ].
                     ].
                 ] ifFalse:[
                     mthd notNil ifTrue:[
                         (ParseTreeSearcher methodIsSetterMethod:mthd) ifTrue:[
                             setOfTypes add:receiverClass.
-                            ^ self
+                            ^ setOfTypes
                         ]
                     ]
                 ]
@@ -4643,14 +4840,14 @@
         and:[ (valClass := Smalltalk classNamed:(msgSelector copyFrom:3)) notNil ]
         ) ifTrue:[
             setOfTypes add:valClass.
-            ^ self
+            ^ setOfTypes
         ].    
 
         ((msgSelector startsWith:'is')
         and:[ (valClass := Smalltalk classNamed:(msgSelector copyFrom:3)) notNil ]
         ) ifTrue:[
             setOfTypes add:True. "/ Boolean - not boolean; it does not contain the full protocol (would not find ifTrue:)
-            ^ self.
+            ^ setOfTypes.
         ].    
 
         #(
@@ -4665,7 +4862,7 @@
         ) pairWiseDo:[:sel :clsName |
             msgSelector == sel ifTrue:[ 
                 setOfTypes add:(Smalltalk at:clsName).
-                ^ self.
+                ^ setOfTypes.
             ].
         ].
 
@@ -4673,74 +4870,39 @@
             "/ assume integer
 
             setOfTypes add:Integer.
-            ^ self
+            ^ setOfTypes
         ].
         ( #( + - * // \\ ) includes:msgSelector) ifTrue:[
             "/ assume numeric
 
             setOfTypes add:Number.
-            ^ self
+            ^ setOfTypes
         ].
         msgSelector == #/ ifTrue:[
             ((receiverClasses ? #()) contains:[:cls | cls includesBehavior:Number]) ifTrue:[
                 setOfTypes add:Number.
-                ^ self.
+                ^ setOfTypes.
             ].
             msgArg1 := expr arg1.
             arg1Classes := ((self classesOfNode:msgArg1) ? #()).
             (arg1Classes contains:[:cls | cls includesBehavior:Number]) ifTrue:[
                 setOfTypes add:Number.
-                ^ self
+                ^ setOfTypes
             ].    
         ].    
         ( #( construct: / ) includes:msgSelector) ifTrue:[
             ((receiverClasses ? #()) contains:[:cls | cls includesBehavior:Filename]) ifTrue:[
                 setOfTypes add:Filename.
-                ^ self
+                ^ setOfTypes
             ].
         ].    
     ].
-    ^ nil
+    ^ setOfTypes
 !
 
-classOfNode:aNode
-    "returns the class of a receiver, if it is well-known.
-     Otherwise nil (either unknown, or multiple possibilities)
-     When showing possible completions for a message,
-     it is a good idea to know what the kind receiver is."
-
-    | classes |
-
-    classes := self classesOfNode:aNode.
-    classes size == 1 ifTrue:[
-        ^ classes anElement
-    ].
-    ^ nil
-!
-
-classesFromAssignmentTo:varName in:aTree
-    |classesFromAssignments|
+addClassesOfInstVarNamed:varName inClass:aClass to:setOfTypes
+    |instIndex|
     
-    classesFromAssignments := Set new.
-    "/ assignments...
-    aTree allAssignmentNodesDo:[:eachAssignmentNode |
-        |exprCls leftSide|
-
-        leftSide := eachAssignmentNode variable.
-        leftSide name = varName ifTrue:[
-            exprCls := self classOfNode:eachAssignmentNode value.
-            exprCls notNil ifTrue:[ 
-                classesFromAssignments add:exprCls
-            ]
-        ]
-    ].
-    ^ classesFromAssignments.
-!
-
-classesOfInstVarNamed:varName inClass:aClass
-    |setOfTypes instIndex|
-    
-    setOfTypes := IdentitySet new.
     instIndex := aClass instVarIndexFor:varName.
 
     "/ look for instances
@@ -4810,23 +4972,42 @@
     ^ setOfTypes
 !
 
+classOfNode:aNode
+    "returns the class of a receiver, if it is well-known.
+     Otherwise nil (either unknown, or multiple possibilities)
+     When showing possible completions for a message,
+     it is a good idea to know what the kind receiver is."
+
+    | classes |
+
+    classes := self classesOfNode:aNode.
+    classes size == 1 ifTrue:[
+        ^ classes anElement
+    ].
+    ^ nil
+!
+
+classesFromAssignmentTo:varName in:aTree
+    ^ self addClassesFromAssignmentTo:varName in:aTree to:IdentitySet new
+!
+
+classesOfInstVarNamed:varName inClass:aClass
+    ^ self addClassesOfInstVarNamed:varName inClass:aClass to:(IdentitySet new)
+!
+
 classesOfNode:aNode
-    "returns the set of possible classes of a receiver.
+    "returns the set of possible classes of a parsenode.
      or nil if unknown.
      When showing possible completions for a message,
      it is a good idea to know what the kind receiver is."
 
-    | setOfTypes|
-
-    setOfTypes := Set new.
-    self addClassesOfExpression:aNode inClass:classOrNil to:setOfTypes.
-    ^ setOfTypes
+    ^ self addClassesOfExpression:aNode inClass:classOrNil to:(IdentitySet new).
 !
 
 valueAndKindOfVariable:aVariableName
     "when showing possible completions for a variable,
      it is a good idea to know what the reveiver's value is.
-     Sigh - returns nil both if unknown AND if a real nil is there."
+     Sigh - returns nil as value both if unknown AND if a real nil is there"
 
     |nodeVal con privateClass pool|
 
@@ -4868,7 +5049,8 @@
 
     aVariableName = 'self' ifTrue:[
         (classOrNil notNil and:[classOrNil isMeta]) ifTrue:[
-            ^ { classOrNil theNonMetaclass . #pseudoVar }
+            ^ { classOrNil "theNonMetaclass" . #pseudoVar }
+            "/ ^ { classOrNil . #pseudoVar }
         ].
         contextOrNil notNil ifTrue:[
             ^ { contextOrNil receiver . #pseudoVar } 
@@ -4877,6 +5059,7 @@
     ].
 
     contextOrNil notNil ifTrue:[
+        "/ in the debugger, we know more
         con := contextOrNil.
         [ con notNil ] whileTrue:[
             "/ a local in the context?