#UI_ENHANCEMENT by cg
authorClaus Gittinger <cg@exept.de>
Sun, 01 May 2016 18:19:51 +0200
changeset 5040 f02e9db9841e
parent 5039 8199b9455761
child 5041 db886e8fb099
#UI_ENHANCEMENT by cg class: DoWhatIMeanSupport much improved completions
DoWhatIMeanSupport.st
--- a/DoWhatIMeanSupport.st	Sun May 01 09:46:40 2016 +0200
+++ b/DoWhatIMeanSupport.st	Sun May 01 18:19:51 2016 +0200
@@ -1078,7 +1078,10 @@
 !
 
 isKey:k1 nextTo:k2 onKeyboard:keys
-    "return true, if k1 and k2 are adjacent keys on the keyboard defined by keys"
+    "return true, if k1 and k2 are adjacent keys on the keyboard defined by keys.
+     This is used to specially priorize plausible typing errors of adjacent keys.
+     (typo checker uses a modified levenshtein, 
+      in which keys next to each other are valued differently)"
 
     |row1 row2 col1 col2|
 
@@ -1093,6 +1096,8 @@
      self isKey:$a nextTo:$q
      self isKey:$a nextTo:$x
     "
+
+    "Modified (comment): / 01-05-2016 / 12:19:24 / cg"
 !
 
 keyboard
@@ -1833,11 +1838,30 @@
 
     aNode isMessage ifTrue:[
         nodeSelector := aNode selector.
+
+        "/ heuristic: quickly assume boolean for some:
+        (
+            #( 
+                isNil notNil isEmptyOrNil notEmptyOrNil
+                > >= < <= = == ~ ~=
+                isInteger isNumber isArray
+                knownAsSymbol
+            ) includes:nodeSelector
+        ) ifTrue:[
+            ^ { True } "/ use True, because boolean does not include the full protocol
+        ].
+
         nodeReceiver := aNode receiver.
 
         "/ some hardwired knowlegde here
         receiverClass := self classOfNode:nodeReceiver.
         receiverClass notNil ifTrue:[
+            nodeSelector == #theNonMetaclass ifTrue:[  
+                ^ { receiverClass theNonMetaclass class }
+            ].
+            nodeSelector == #theMetaclass ifTrue:[  
+                ^ { receiverClass theMetaclass class }
+            ].
             nodeSelector == #class ifTrue:[
                 ^ { receiverClass class }
             ].
@@ -1863,28 +1887,24 @@
                 ]
             ].
         ].
-        classOrNil notNil ifTrue:[
-            (nodeReceiver isSelf and:[nodeSelector = #'class']) ifTrue:[
-                ^ { classOrNil class }
+
+        #(
+            asFilename              Filename
+            asOrderedCollection     OrderedCollection
+            asArray                 Array
+            asSet                   Set
+            size                    SmallInteger
+            hash                    SmallInteger
+            identityHash            SmallInteger
+            class                   Class
+            theMetaclass            Metaclass
+            theNonMetaclass         Class
+        ) pairWiseDo:[:sel :clsName |
+            nodeSelector == sel ifTrue:[ 
+                ^ { Smalltalk at:clsName } 
             ].
         ].
 
-        (nodeSelector = #'asFilename') ifTrue:[
-            ^ { Filename }
-        ].
-        (nodeSelector = #'asOrderedCollection') ifTrue:[
-            ^ { OrderedCollection }
-        ].
-        (nodeSelector = #'asArray') ifTrue:[
-            ^ { Array }
-        ].
-        (nodeSelector = #'asSet') ifTrue:[
-            ^ { Set }
-        ].
-        (nodeSelector = #'size') ifTrue:[
-            ^ { SmallInteger }
-        ].
-
         "/ some wellknown boolean returners (need better type inference here)
         (#( isNil notNil not isEmptyOrNil notEmptyOrNil notEmpty isEmpty
             isBehavior isMeta
@@ -1900,13 +1920,10 @@
             "/ assume numeric
             ^ { Number }
         ].
-
-        ( #( class theMetaclass theNonMetaclass ) includes:nodeSelector) ifTrue:[
-            "/ assume behavior
-            ^ { Behavior }
-        ].
     ].
     ^ nil
+
+    "Modified: / 01-05-2016 / 12:33:20 / cg"
 !
 
 codeCompletionForLiteralSymbol:nodeOrNil element:tokenOrNil considerAll:considerAll into:actionBlock
@@ -2044,21 +2061,21 @@
      receiverNodeClassIfKnown 
      offerParenthisationAroundNode parenthesisAroundIndex
      parentNodeToParenthesize|
-
+ 
     "/ Transcript show:'node '; show:node; show:' ; '.
     "/ Transcript show:'msg in '; show:methodOrNil; show:' / '; showCR:classOrNil.
-
+ 
     offerParenthisationAroundNode := nil.
-
+ 
     "/ node at:1
-
+ 
     findBest := 
         [:node :selector |
             |srchClasses bestSelectors bestPrefixes
              allMessagesSentToVariable classesImplementingAllMessages|
-
+ 
             srchClasses := self classesOfNode:node.
-
+ 
             srchClasses isEmptyOrNil ifTrue:[
                 node isVariable ifTrue:[
                     allMessagesSentToVariable := Set new.
@@ -2092,7 +2109,7 @@
             ] ifFalse:[    
                 srchClasses do:[:srchClass |
                     |bestForThisClass|
-
+ 
                     bestForThisClass := Parser findBest:50 selectorsFor:selector in:srchClass forCompletion:true.
                     bestForThisClass := self
                                         withoutSelectorsUnlikelyFor:srchClass
@@ -2101,36 +2118,36 @@
                     bestSelectors addAll:bestForThisClass.
                 ].
             ].
-            (bestSelectors includes:selector) ifTrue:[
-                bestSelectors := bestSelectors select:[:sel | sel size > selector size].
-            ].
+            "/ remove the already typed-in selector itself, in case.
+            bestSelectors remove:selector ifAbsent:[].
             bestSelectors := bestSelectors asOrderedCollection.
             bestSelectors
         ].
-
+ 
     selector := node selector.
     lcSelector := selector asLowercase.
     parentNode := node parent.
     nodeReceiver := node receiver.
-
+ 
     "/ if there is already space before the cursor, and the parent node is not a message,
     "/ do not attempt to complete the current message.
     "/ If it is a message, we will look for parent-message completion also below (best2 stuff)
     (codeView characterBeforeCursor ? $ ) isSeparator ifTrue:[
         selector isKeyword ifFalse:[
-            (parentNode notNil and:[ parentNode isMessage ]) ifFalse:[
-                ^ self.
-            ].
+            ^ self
+"/            (parentNode notNil and:[ parentNode isMessage ]) ifFalse:[
+"/                ^ self.
+"/            ].
         ].
     ].
-
+ 
     "/ only do this if the node-message has no parents around
     node parentheses isEmptyOrNil ifTrue:[
         bestSelectors := findBest value:nodeReceiver value:selector.
     ] ifFalse:[
         bestSelectors := OrderedCollection new.
     ].
-
+ 
     "/ if the receiver is a real variable,
     "/ we can look for other messages being sent to that variable in the current method.
     "/ Also, if there are assignment to it (like constants or '<class> new'), use that as a hint...
@@ -2139,74 +2156,82 @@
         and:[ nodeReceiver isSelf not
         and:[ nodeReceiver isSuper not ]]])
     ifTrue:[
-        |classesFromAssignmentsToReceiver otherMessagesToReceiver possibleClasses possibleClassesFromOtherSends|
-
-        classesFromAssignmentsToReceiver := 
-            tree allAssignmentNodes
-                collect:[:eachAssignmentNode |
-                            |cls|
-                            (nodeReceiver = eachAssignmentNode variable
-                                and:[ (cls := self classOfNode:eachAssignmentNode value) notNil ]
-                            ) ifTrue:[ cls ] ifFalse:[ nil ]
-                        ]
-                thenSelect:[:classOrNil | classOrNil notNil].
-
-        possibleClasses := classesFromAssignmentsToReceiver.
-
-        otherMessagesToReceiver := Set new.
-        tree allMessageNodesDo:[:eachMessageNode |
-            (nodeReceiver = eachMessageNode receiver
-                and:[ selector ~= eachMessageNode selector]
-            ) ifTrue:[   
-                otherMessagesToReceiver add:eachMessageNode selector
+        |receiverName classesFromAssignmentsToReceiver otherMessagesToReceiver possibleClasses possibleClassesFromOtherSends|
+ 
+        receiverName := nodeReceiver name.
+ 
+        classesFromAssignmentsToReceiver := Set new.
+        "/ assignments...
+        tree allAssignmentNodesDo:[:eachAssignmentNode |
+            |exprCls leftSide|
+ 
+            leftSide := eachAssignmentNode variable.
+            leftSide name = receiverName ifTrue:[
+                exprCls := self classOfNode:eachAssignmentNode value.
+                exprCls notNil ifTrue:[ 
+                    classesFromAssignmentsToReceiver add:exprCls
+                ]
             ]
         ].
-        otherMessagesToReceiver notEmpty ifTrue:[
-            possibleClassesFromOtherSends :=
-                Smalltalk 
-                    allClassesForWhich:[:cls |
-                        cls isLoaded
-                        and:[ otherMessagesToReceiver
-                                conform:[:eachSelectorSent | cls includesSelector: "canUnderstand:" eachSelectorSent]]
-                    ].
-            possibleClasses := possibleClasses , possibleClassesFromOtherSends.
+        possibleClasses := classesFromAssignmentsToReceiver.
+ 
+        possibleClasses isEmpty ifTrue:[
+            "/ messages sent
+            otherMessagesToReceiver := Set new.
+            tree allMessageNodesDo:[:eachMessageNode |
+                (nodeReceiver = eachMessageNode receiver
+                    and:[ selector ~= eachMessageNode selector]
+                ) ifTrue:[   
+                    otherMessagesToReceiver add:eachMessageNode selector
+                ]
+            ].
+            otherMessagesToReceiver notEmpty ifTrue:[
+                "/ classes which respond to all
+                possibleClassesFromOtherSends :=
+                    Smalltalk 
+                        allClassesForWhich:[:cls |
+                            cls isLoaded
+                            and:[ otherMessagesToReceiver
+                                    conform:[:eachSelectorSent | cls canUnderstand:eachSelectorSent]]
+                        ].
+                possibleClasses := possibleClasses , possibleClassesFromOtherSends.
+            ].
         ].
-
+ 
         "/ if the receiver is a classVar/classInstVar,
         "/ include the class of its current value and UndefinedObject.
         "/ This helps to complete class methods and (lazy) initializer code.
         (classOrNil notNil) ifTrue:[
             |tryValue currentValue|
-
+ 
             tryValue := false.
-            (classOrNil theNonMetaclass allClassVarNames includes: nodeReceiver name) ifTrue:[
+            (classOrNil theNonMetaclass allClassVarNames includes: receiverName) ifTrue:[
                 tryValue := true.
-                currentValue := classOrNil theNonMetaclass classVarAt:nodeReceiver name.
+                currentValue := classOrNil theNonMetaclass classVarAt:receiverName.
             ] ifFalse:[
-                (classOrNil isMeta and:[ classOrNil allInstVarNames includes: nodeReceiver name ]) ifTrue:[
+                (classOrNil isMeta and:[ classOrNil allInstVarNames includes: receiverName ]) ifTrue:[
                     tryValue := true.
-                    currentValue := classOrNil theNonMetaclass instVarNamed:nodeReceiver name.
+                    currentValue := classOrNil theNonMetaclass instVarNamed:receiverName.
                 ].
             ].
             tryValue ifTrue:[
-                currentValue notNil ifTrue:[ possibleClasses := { UndefinedObject } , possibleClasses ].
                 possibleClasses := { currentValue class } , possibleClasses.
             ].
         ].
-
+ 
         (possibleClasses notEmpty and:[possibleClasses size < 15]) ifTrue:[
             bestSelectors :=
                 (possibleClasses 
                     collectAll:[:eachClass |
                         Parser findBest:30 selectorsFor:selector in:eachClass forCompletion:true.
                     ] as:Set) asOrderedCollection.
-
+ 
             "/ if any of those is a prefix-keyword of the selector,
             "/ do not offer it (i.e. ifTrue:ifFalse: is already present, don't offer ifTrue:ifFalse: again.
             bestSelectors := bestSelectors reject: [:sel | (selector startsWith: sel) or: [selector endsWith: sel]].
         ].
     ].                                                                            
-
+ 
     "/ if we are behind a keyword messages colon,
     "/ only look for matching prefix selectors;
     "/ also, a good completion is to insert an argument;
@@ -2214,21 +2239,22 @@
     "/ Array new:1
     selector isKeyword ifTrue:[
         (node arguments size = selector numArgs) ifTrue:[
-            offerParenthisationAroundNode := node
+            offerParenthisationAroundNode := node. 
+Transcript show:'2:'; showCR:node.
         ].
-
+ 
         codeView characterBeforeCursor == $: ifTrue:[
             (bestSelectors select:[:sel | sel asLowercase startsWith:lcSelector]) isEmpty ifTrue:[
                 "/ nothing better around
                 |argIndex argNames impls|
-
+ 
                 argIndex := node selectorParts size.
                 argNames := Set new.
                 impls := Smalltalk allImplementorsOf:selector.
                 impls size < 10 ifTrue:[
                     impls do:[:eachImplClass |
                         |mthd argName|
-
+ 
                         mthd := (eachImplClass compiledMethodAt:selector).
                         argName := (mthd methodArgNames ? #()) at:argIndex ifAbsent:nil.
                         argName notNil ifTrue:[
@@ -2250,9 +2276,10 @@
             bestSelectors := bestSelectors select:[:sel | sel isUnarySelector ]
         ]
     ].
-
-    bestSelectors := bestSelectors asOrderedCollection sort:[:a :b | a size < b size].
-
+ 
+"/    bestSelectors := bestSelectors asOrderedCollection.
+"/    bestSelectors sort:[:a :b | a size < b size].
+ 
     (selector isUnarySelector and:[ parentNode notNil and:[ parentNode isMessage ]]) ifTrue:[
         (selector2 := parentNode selector) isKeywordSelector ifTrue:[
             "/ if its a unary message AND the parent is a keyword node, look for parent completion too.
@@ -2262,14 +2289,15 @@
             bestSelectors2 := bestSelectors2 select:[:sel | sel isKeywordSelector and:[ sel startsWith:selector2]].
             bestSelectors2 := bestSelectors2 asOrderedCollection sort:[:a :b | a size < b size].
             bestSelectors := bestSelectors reject:[:sel | bestSelectors2 includes:sel].
-
+ 
             "/ if the parent has a valid selector, offer parenthization
             (Smalltalk someImplementorOf:selector2) notNil ifTrue:[
                 offerParenthisationAroundNode := parentNode.
+                "/ Transcript show:'2:'; showCR:parentNode.
             ].
         ] ifFalse:[
             |kwSels|
-
+ 
             "/ if its a unary message AND the parent is a unary or binary node, try again, sending the partial message
             "/ as a keyword to the parent node.
             "/ this is the case when after "foo binOp bar if", which should include ifTrue: in the result.
@@ -2293,14 +2321,14 @@
             "/              /
             "/             /
             "/           arg
-
+ 
             kwSels := findBest value:parentNode value:selector.
             kwSels := kwSels select:[:sel | sel isKeywordSelector].
-
+ 
             kwSels := kwSels asOrderedCollection sort:[:a :b | a size < b size].
-
+ 
             bestSelectors := bestSelectors reject:[:sel | kwSels includes:sel].
-
+ 
             "/ these need to go to bestSelectors (see editAction)
             parentNodeClassIfKnown := self classOfNode:parentNode.
             (parentNodeClassIfKnown notNil and:[ parentNodeClassIfKnown includesBehavior: Boolean ]) ifTrue:[
@@ -2315,7 +2343,7 @@
                             withoutSelectorsUnlikelyFor:parentNodeClassIfKnown
                             from:kwSels
                             forPartial:selector.
-
+ 
                 "/ put keyword selectors in front, because they are very likely
                 bestSelectors := kwSels , bestSelectors.
             ] ifFalse:[
@@ -2324,7 +2352,7 @@
             ].
         ]
     ].
-
+ 
     (selector isUnarySelector and:[ node isMessage ]) ifTrue:[
         receiverNodeClassIfKnown := self classOfNode:nodeReceiver.
         (receiverNodeClassIfKnown notNil and:[ receiverNodeClassIfKnown includesBehavior: Boolean ]) ifTrue:[
@@ -2337,10 +2365,11 @@
                                 forPartial:selector.
         ].
     ].
-    (selector isUnarySelector
-    and:[ parentNode notNil
-    and:[ parentNode isMessage
-    and:[ (parentNode selector isUnarySelector not) ]]]) ifTrue:[
+    (parentNode notNil 
+        and:[ parentNode isMessage
+        and:[ ((parentNode selector isUnarySelector not) and:[selector isUnarySelector])
+          or:[ ((parentNode selector isKeywordSelector) and:[selector isBinarySelector]) ]]]
+    ) ifTrue:[
         "/ completing an already existing keyword or binary message with something starting with
         "/ if, and, or or while.
         "/ Here, offer a special completion which inserts parenthesis / brackets around the already
@@ -2348,15 +2377,19 @@
         "/    expr wh
         "/ ->
         "/    [expr] whileXX:[]
-        ((
+        true "((
             #( 'ifTrue' 'ifFalse' 'and' 'or' 'do' 'keysAndValuesDo' 'whileTrue' 'whileFalse' 'ensure' 'on')
-        ) contains:[:part | part startsWith:selector]) ifTrue:[
+        ) contains:[:part | part startsWith:selector])" ifTrue:[
             (Smalltalk someImplementorOf:parentNode selector) notNil ifTrue:[
                 |selsP selsB|
-
+ 
                 selsP := #( 'ifTrue:' 'ifFalse:' 'and' 'or' 'do' 'keysAndValuesDo' )
                             select:[:sel | sel startsWith:selector]
                             thenCollect:[:sel | '(',parentNode selector,') ',sel].
+                ( #( 'whileTrue:' 'whileFalse:' 'ensure:' 'on:do:' ) contains:[:sel | sel startsWith:selector]) 
+                ifFalse:[
+                    selsP := selsP copyWith:'(',parentNode selector,') ',selector  
+                ]. 
                 selsB := #( 'whileTrue:' 'whileFalse:' 'ensure:' 'on:do:' )
                             select:[:sel | sel startsWith:selector]
                             thenCollect:[:sel | '[',parentNode selector,'] ',sel].
@@ -2374,7 +2407,7 @@
         ) contains:[:part | part startsWith:selector]) ifTrue:[
             (node receiver isBlock) ifFalse:[
                 |sels|
-
+ 
                 (node receiver isMessage not
                 or:[ (Smalltalk someImplementorOf:node receiver selector) notNil ]) ifTrue:[
                     sels := #( 'whileTrue:' 'whileFalse:' 'ensure:' 'on:do:' )
@@ -2386,7 +2419,7 @@
             ].
         ].
     ].
-
+ 
     allBest := (bestSelectors ? #()) , (bestSelectors2 ? #()).
     allBest sort:
         [:a :b |
@@ -2419,7 +2452,7 @@
     split :=
         [:list :splitHow |
             |part1 part2 all|
-
+ 
             part1 := list select:splitHow.
             part2 := list reject:splitHow.
             part1 isEmpty ifTrue:[
@@ -2442,12 +2475,12 @@
                             (sel asLowercase startsWith:lcSelector) 
                             or:[sel startsWith:selector2]].
     ].
-
+ 
     "/ if receiver is super, always include the method's own selector
     nodeReceiver isSuper ifTrue:[
         (tree isMethod) ifTrue:[
             |mSel|
-
+ 
             mSel := tree selector.
             mSel notNil ifTrue:[
                 (mSel startsWith:selector) ifTrue:[
@@ -2460,23 +2493,36 @@
             ]
         ]
     ].
-
-    allBest := (bestWithParenthesis ? #()) , allBest.
-    allBest isEmptyOrNil ifTrue:[ 
+ 
+    (allBest isEmptyOrNil and:[bestWithParenthesis isEmptyOrNil]) ifTrue:[ 
         ^ self 
     ].
-
-    "/ the one's which are a prefix are moved towards the top of the list
-    allBest := split 
-                    value:allBest 
-                    value:[:sel | sel notNil and:[sel asLowercase startsWith:lcSelector]].
-
-    rememberedNodes notNil ifTrue:[
-        selectorsSentInCode := 
-            (rememberedNodes
-                select:[:node | node isMessage]
-                thenCollect:[:node | node selector]) asSet.
-        selectorsSentInCode remove:selector ifAbsent:[].
+ 
+    "/ see what is aready sent to this variable inside the code
+    nodeReceiver notNil ifTrue:[
+        nodeReceiver isVariable ifTrue:[
+            rememberedNodes notNil ifTrue:[
+                selectorsSentInCode := 
+                    (rememberedNodes
+                        select:[:node | 
+                            node isMessage 
+                                and:[node receiver isVariable
+                                and:[node receiver name = nodeReceiver name]]]
+                        thenCollect:[:node | 
+                            node selector]
+                    ) asSet.
+            ] ifFalse:[
+                selectorsSentInCode := Set new. 
+                tree allMessageNodesDo:[:msg |
+                    (msg receiver isVariable
+                        and:[msg receiver name = nodeReceiver name]
+                    ) ifTrue:[
+                        selectorsSentInCode add:msg selector
+                    ].
+                ].
+                selectorsSentInCode remove:selector ifAbsent:[].
+            ]. 
+        ]. 
     ]. 
     nodeReceiver notNil ifTrue:[
         |classOrNil|
@@ -2490,13 +2536,17 @@
         ]
     ].    
     selectorsImplementedInClass notNil ifTrue:[
-        "/ the one's already sent in the code are moved to the top of the list.
+        "/ the one's implemented in the class itself are moved to the top of the list.
         allBest := split value:allBest value:[:sel | selectorsImplementedInClass includes:sel].
     ].
     selectorsSentInCode notNil ifTrue:[
         "/ the one's already sent in the code are moved to the top of the list.
+        "/ trouble is: parser bails out on error, so most of the time, we only see
+        "/ selectors sent previously. sigh.
         allBest := split value:allBest value:[:sel | selectorsSentInCode includes:sel].
     ].
+ 
+"/ this makes it very slow
 "/false ifTrue:[
 "/    srchClass notNil ifTrue:[
 "/        implClass := srchClass whichClassIncludesSelector:best.
@@ -2515,35 +2565,58 @@
 "/    ].
 "/    self information:info.
 "/].
-
+ 
+    "/ the one's which are a prefix are moved towards the top of the list
+    allBest := split 
+                    value:allBest 
+                    value:[:sel | sel notNil and:[sel asLowercase startsWith:lcSelector]].
+ 
+    "/ heuristic hack:
+    "/ 'i' and 'w' generate lists in which ifXXX / whileXXX are not at the top of the list.
+    "/ we know, that those are most often wanted!!
+    selector size <= 2 ifTrue:[
+        allBest := split 
+                    value:allBest 
+                    value:[:sel | 
+                        #(ifTrue: ifFalse: isNil notNil whileTrue whileFalse) includes:sel
+                    ].
+    ]. 
+
+    self sortUsefulSelectorsIn:allBest. "/cosmetics
+
+    "/ parenthesizers always at the end.
+    bestWithParenthesis notEmptyOrNil ifTrue:[ 
+        allBest := allBest , bestWithParenthesis.
+    ].
+ 
     "/ self at:1 put:#foo
     "/ Array new:10
     offerParenthisationAroundNode notNil ifTrue:[
         allBest := allBest copyWith:( '(',selector,')' ).
         parenthesisAroundIndex := allBest size.
     ].
-
+ 
     editAction :=
         [:index |
-            |crsrPos chosen parentsToInsert action|
-
+            |crsrPos chosen parenthesisToInsert action|
+ 
             action := nil.
             crsrPos := codeView characterPositionOfCursor.
             chosen := allBest at:index.
-
+ 
             chosen ~= selector ifTrue:[
                 (bestWithParenthesis notNil and:[bestWithParenthesis includes:chosen]) ifTrue:[
                     "/ for input like: 
                     "/   chosen at: 10 if
                     "/ put parenthesis around, and add ifTrue/ifFalse
                     "/ i.e.:   (chosen at:10) ifTrue:[]
-
+ 
                     "/ for input like: 
                     "/   a > 10 wh
                     "/ put brackets around and add whileTrue/whileFalse
                     "/ i.e.:   [a > 10] whileTrue:[]
-                    parentsToInsert := chosen first == $( ifTrue:'()' ifFalse:'[]'.
-                    chosen := (chosen copyFrom:(chosen lastIndexOf:parentsToInsert second)+1) withoutSeparators.
+                    parenthesisToInsert := chosen first == $( ifTrue:'()' ifFalse:'[]'.
+                    chosen := (chosen copyFrom:(chosen lastIndexOf:parenthesisToInsert second)+1) withoutSeparators.
                 ] ifFalse:[
                     (offerParenthisationAroundNode notNil and:[index = parenthesisAroundIndex]) ifTrue:[
                         "/ for input like: 
@@ -2558,7 +2631,7 @@
                             ].
                     ]
                 ].
-
+ 
                 action isNil ifTrue:[
                     numArgs := chosen numArgs.
                     (bestSelectors2 notEmptyOrNil and:[bestSelectors2 includes:chosen]) ifTrue:[
@@ -2567,50 +2640,50 @@
                         selectorParts := node selectorParts.
                     ].
                     nSelParts := selectorParts size.
-
+ 
                     newParts := chosen asCollectionOfSubstringsSeparatedBy:$:.
                     newParts := newParts select:[:part | part size > 0].
-
+ 
                     action :=
                         [
                             |positionOfFirstArg newCursorPosition stop checkForArgumentTemplates
                              newPart oldPartialToken start|
-
+ 
                             checkForArgumentTemplates := (selector isUnarySelector and:[chosen isKeywordSelector]).
                             numArgs > nSelParts ifTrue:[
                                 "/ new selector has more arguments; append them
                                 stop := selectorParts last stop.
                                 codeView deleteFromCharacterPosition:stop+1 to:crsrPos-1.
-
+ 
                                 "/ append the rest ...
                                 (numArgs min:newParts size) downTo:(nSelParts+1) do:[:idx |
                                     |newPart|
-
+ 
                                     newPart := newParts at:idx.
                                     newPart := newPart , ':'.
-
+ 
                                     (codeView characterAtCharacterPosition:stop) == $: ifFalse:[
                                         newPart := ':' , newPart.
                                     ].
                                     newPart := (codeView characterAtCharacterPosition:stop) asString , newPart.
-
+ 
                                     codeView replaceFromCharacterPosition:stop to:stop with:newPart.
                                     "/ remember the leftMost replacement's end as new cursor position
                                     newCursorPosition := stop + newPart size
                                 ].
                                 checkForArgumentTemplates := true.
                             ].
-
+ 
                             "/ replace existing parts
                             (nSelParts min:newParts size) downTo:1 do:[:idx |
                                 |skipColon|
-
+ 
                                 skipColon := 0.
                                 newPart := newParts at:idx.
                                 oldPartialToken := selectorParts at:idx.
                                 start := oldPartialToken start.
                                 stop := oldPartialToken stop.
-
+ 
                                 (chosen endsWith:$:) ifTrue:[
                                     (codeView characterAtCharacterPosition:stop+1) == $: ifFalse:[
                                         newPart := newPart , ':'.
@@ -2622,7 +2695,7 @@
                                         newPart := newPart , ':'
                                     ] ifFalse:[
                                         |nextChar|
-
+ 
                                         nextChar := codeView characterAtCharacterPosition:stop+1.
                                         nextChar isSeparator ifFalse:[
                                             nextChar == $. ifFalse:[
@@ -2634,13 +2707,13 @@
             "/                        ] ifFalse:[
             "/                            codeView replaceFromCharacterPosition:start to:stop with:newPart.
                                 ].
-
+ 
                                 oldPartialToken value ~= newPart ifTrue:[
                                     codeView replaceFromCharacterPosition:start to:stop with:newPart.
-
+ 
                                     oldLen := stop - start + 1.
                                     newLen := newPart size.
-
+ 
                                     "/ codeView selectFromCharacterPosition:start+oldLen to:start+newLen-1.
                                     "/ remember the leftMost replacement's end as new cursor position
                                     newCursorPosition := start + newPart size + skipColon. "/ (newLen-oldLen) + 1.
@@ -2652,45 +2725,108 @@
                                 codeView cursorRight.  "/ avoid going to the next line !!
                             ].
                             codeView dontReplaceSelectionOnInput.
-
+ 
                             checkForArgumentTemplates ifTrue:[
                                 "/ add opening brackets, etc.
                                 self insertAdditonalStuffAfterSelector:chosen.
                             ].
-                            parentsToInsert notNil ifTrue:[
+                            parenthesisToInsert notNil ifTrue:[
                                 |sav pos|
-
+ 
                                 sav := codeView characterPositionOfCursor-1.
                                 "/ check if already parenthized
-                                node receiver hasParentheses ifTrue:[
-                                    pos := node receiver parentheses first first.
+                                parentNodeToParenthesize hasParentheses ifTrue:[
+                                    pos := parentNodeToParenthesize parentheses first first.
                                     codeView selectFromCharacterPosition:pos to:pos.
-                                    codeView replaceSelectionBy:(parentsToInsert copyFirst:1) asString.
-
-                                    pos := node receiver parentheses first last.
+                                    codeView replaceSelectionBy:(parenthesisToInsert copyFirst:1) asString.
+ 
+                                    pos := parentNodeToParenthesize parentheses first last.
                                     codeView selectFromCharacterPosition:pos to:pos.
-                                    codeView replaceSelectionBy:(parentsToInsert copyLast:1) asString.
+                                    codeView replaceSelectionBy:(parenthesisToInsert copyLast:1) asString.
                                     codeView cursorToCharacterPosition:sav; cursorRight
                                 ] ifFalse:[
-                                    codeView insertString:(parentsToInsert copyLast:1) atCharacterPosition:node receiver stop+1.
-                                    codeView insertString:(parentsToInsert copyFirst:1) atCharacterPosition:node receiver "parentNode" start.
+                                    codeView insertString:(parenthesisToInsert copyLast:1) atCharacterPosition:node receiver stop+1.
+                                    codeView insertString:(parenthesisToInsert copyFirst:1) atCharacterPosition:parentNodeToParenthesize start.
                                     codeView cursorToCharacterPosition:sav+2; cursorRight
                                 ].
                             ].
                         ].
                     ].
-
+ 
                 codeView
                     undoableDo:action
                     info:'Completion'.
             ].
         ].
-
+ 
     actionBlock value:allBest value:editAction value:nil.
 
     "Created: / 10-11-2006 / 13:18:27 / cg"
     "Modified: / 16-02-2010 / 10:33:48 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 01-05-2016 / 09:46:27 / cg"
+    "Modified: / 01-05-2016 / 17:48:54 / cg"
+!
+
+codeCompletionForMessageTo:node into:actionBlock
+    "find good suggestions for a message send to node, with no input yet"
+    
+    |knownClass suggestions selectorsImplementedInClass mostUseful|
+ 
+    (knownClass := self classOfNode:node) isNil ifTrue:[^ self].
+
+    selectorsImplementedInClass := Set new.
+
+    knownClass withAllSuperclassesDo:[:cls |
+        cls ~~ Object ifTrue:[
+            selectorsImplementedInClass addAll:cls selectors.
+        ]    
+    ].
+
+    knownClass isMeta ifTrue:[
+        selectorsImplementedInClass := 
+            selectorsImplementedInClass reject:[:sel |
+                |mthd|
+
+                mthd := knownClass lookupMethodFor:sel.
+                mthd notNil and:[mthd category startsWith: 'documentation']
+            ].
+
+        knownClass theNonMetaclass isAbstract ifTrue:[
+            mostUseful := selectorsImplementedInClass select:[:sel |
+                            knownClass implements:sel
+                          ]
+        ] ifFalse:[
+            mostUseful := selectorsImplementedInClass select:[:sel |
+                                |mthd|
+
+                                mthd := knownClass lookupMethodFor:sel.
+                                mthd notNil and:[mthd category startsWith: 'instance']
+                          ].
+        ]
+    ] ifFalse:[
+        mostUseful := 
+            #(
+                "/ blocks
+                ifTrue: ifFalse: whileTrue: whileFalse: on:do: ensure: 
+                whileTrue whileFalse loop
+                "/ any
+                isNil notNil isEmpty notEmpty 
+            ).
+    ].
+
+    mostUseful notNil ifTrue:[
+        suggestions := 
+            (selectorsImplementedInClass select:[:sel | mostUseful includes:sel]) asNewOrderedCollection sort
+            ,
+            (selectorsImplementedInClass reject:[:sel | mostUseful includes:sel]) asNewOrderedCollection sort.
+    ] ifFalse:[
+        suggestions := selectorsImplementedInClass asNewOrderedCollection sort.
+    ].
+
+    self sortUsefulSelectorsIn:suggestions. "/cosmetics
+    actionBlock value:suggestions value:nil value:nil.
+
+    "Created: / 01-05-2016 / 17:01:21 / cg"
+    "Modified: / 01-05-2016 / 18:13:50 / cg"
 !
 
 codeCompletionForMethodSpec:node
@@ -3037,20 +3173,25 @@
      globalFactor localFactor selectorOfMessageToNode implementors argIdx namesUsed kwPart
      editAction suggestions nameIsOK longerNames setOfNames otherArgNames
      suggestionsWithInfo|
-
+ 
     "/ Transcript show:'var in '; show:methodOrNil; show:' / '; showCR:classOrNil.
     classOrNil notNil ifTrue:[
         nonMetaClass := classOrNil theNonMetaclass.
     ].
-
+ 
     nm := node name.
-
+ 
+    crsrPos := codeView characterPositionOfCursor.
+ 
     "/ if we are behind the variable and a space has already been entered,
     "/ the user is probably looking for a message selector.
     "/ If the variable represents a global, present its instance creation messages
-    crsrPos := codeView characterPositionOfCursor.
-    char := codeView characterAtCharacterPosition:crsrPos-1.
-    char isSeparator ifTrue:[
+    char := codeView characterBeforeCursor. 
+    "/ char := codeView characterAtCharacterPosition:crsrPos-1. -wrong if beyond EOL 
+    "/ Transcript show:'crsrPos: '; showCR:crsrPos.
+    "/ Transcript show:'varchar: '; showCR:char.
+
+    char == Character space ifTrue:[
         nm knownAsSymbol ifTrue:[
             classOrNil isNil ifTrue:[
                 nodeVal := Smalltalk at:nm asSymbol.
@@ -3077,7 +3218,7 @@
                             ]
                         ]
                     ].
-
+ 
                 selectors := selectors1 order sort , #('-') , selectors2 order sort.
                 editAction :=
                     [:answer |
@@ -3090,15 +3231,12 @@
                             ]
                             info:'completion'.
                     ].
-                actionBlock
-                    value:selectors
-                    value:editAction
-                    value:nil.
+                actionBlock value:selectors value:editAction value:nil.
                 ^ self.
             ].
         ].
     ].
-
+ 
     parent := node parent.
     (parent notNil and:[parent isMessage]) ifTrue:[
         node == parent receiver ifTrue:[
@@ -3148,9 +3286,10 @@
             |distanceComputeBlock|
 
             distanceComputeBlock := (getDistanceComputeBlockWithWeight value:factor).
-            (eachNames includes:nm) ifTrue:[nameIsOK := true].
             eachNames do:[:nameToAdd |
-                (nameToAdd ~= nm) ifTrue:[  "/ not again
+                (nameToAdd = nm) ifTrue:[
+                    nameIsOK := true
+                ] ifFalse:[ "/ not again
                     (variablesAlreadyAdded includes:nameToAdd) ifFalse:[  "/ not again
                         variablesAlreadyAdded add:nameToAdd.
                         allVariables add:nameToAdd.
@@ -3245,7 +3384,7 @@
         addWithFactorBlock value:(codeView previousReplacements collect:[:p | p value asString]) value:(1.3 * localFactor).
     ] ifFalse:[
         "/ locals in the block/method
-        |names  nameSpace|
+        |names nameSpace|
 
         names := OrderedCollection withAll:node allVariablesOnScope.
         setOfNames := Set withAll:names.
@@ -3267,6 +3406,7 @@
                 ].
                 "/ (setOfNames includesAll:(eachScope allDefinedVariables)) ifFalse:[ self halt].
             ].
+
             rememberedScopeNodes do:[:eachScope |
                 eachScope variableNodesDo:[:var |
                     (setOfNames includes:var name) ifFalse:[
@@ -3472,10 +3612,13 @@
         longerNames size < 30 ifTrue:[
             longerNames := allTheBest select:[:assoc | assoc key includesString:nm caseSensitive:false].
         ].
-        longerNames notEmpty ifTrue:[
-            allTheBest := longerNames.
+        longerNames isEmpty ifTrue:[
+            "/ no better name
+            ^ self
         ].
+        allTheBest := longerNames.
     ].
+
     allTheBest size > 20 ifTrue:[
         allTheBest := allTheBest copyTo:20.
 "/        "/ remove all those which are below some threshold or are a prefix
@@ -3553,13 +3696,34 @@
     suggestionsWithInfo := 
         suggestions 
             collect:[:eachName |
-                |val|
-
-                val := self valueOfVariable:eachName.
-                val isNil ifTrue:[
+                |val kind valAndKind printString|
+
+                valAndKind := self valueAndKindOfVariable:eachName.
+                valAndKind isNil ifTrue:[
                     eachName
                 ] ifFalse:[
-                    eachName,' (',val class name,')'
+                    val := valAndKind first.
+                    kind := valAndKind second.
+
+                    val isBehavior ifTrue:[
+                        val isLoaded ifFalse:[
+                            eachName,' ( ', ('autoloaded class in ',(val category ? 'unknown category')) allItalic,' )'
+                        ] ifTrue:[
+                            val isNameSpace ifTrue:[
+                                eachName,' ( ', 'namespace' allItalic,' )'
+                            ] ifFalse:[
+                                eachName,' ( ', ('class in ',(val category ? 'unknown category')) allItalic,' )'
+                            ]
+                        ]
+                    ] ifFalse:[
+                        "/ Parser findBest:30 selectorsFor:'isLite' in:nil forCompletion:true
+
+                        (val isLiteral and:[ (printString := val printString) size < 15 ]) ifTrue:[
+                            eachName,' ( ',printString allItalic,' )'
+                        ] ifFalse:[
+                            eachName,' ( ',val classNameWithArticle allItalic,' )'
+                        ].
+                    ].
                 ].
             ].
 
@@ -3567,7 +3731,7 @@
 
     "Created: / 10-11-2006 / 13:16:33 / cg"
     "Modified: / 16-02-2010 / 10:13:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
-    "Modified: / 30-07-2013 / 08:36:11 / cg"
+    "Modified: / 01-05-2016 / 17:25:27 / cg"
 !
 
 findNodeForInterval:interval in:source
@@ -3639,15 +3803,31 @@
 !
 
 findNodeForInterval:interval in:source allowErrors:allowErrors mustBeMethod:mustBeMethod mustBeExpression:mustBeExpression
-    "parse it as expression or method;
-     if mustBeMethod is true, do not try a regular expressions (as in a workspace);
-     if mustBeExpression is true, do not try method"
+    "parse source, and find the node which is in the given interval (typically a selection or a word in the source).
+
+     parse it as expression or method;
+        if mustBeMethod is true, do not try as expression;
+        if mustBeExpression is true, do not try as method
+     expression syntax parsing is done in workspaces (doIt).
+
+     Big hack as workaround a limitation of RBParser:
+     in case of an error, the parent chain of a node is usually not yet set.
+     (because the code is written as: 
+        parentNode addChild:(self parseChild)
+     and the parent-chain of the parsed child is set in addChild).
+     But:
+        when doing code completion, having invalid syntax to parse is the normal case.
+     Workaround:
+        remember created nodes as the parse proceeds, and remember them.
+        Thus, I have the parent chain.
+    "
 
     |intersectingNodes smallestIntersectingNode firstIntersectingNode
      lastIntersectingNode onErrorBlock
      nodeGenerationHook parserClass parser currentScopeNodes bestNode|
 
     interval isEmpty ifTrue: [^ nil].
+
     languageOrNil notNil ifTrue:[
         parserClass := languageOrNil parserClass.
     ] ifFalse:[
@@ -3805,6 +3985,7 @@
 
     "Created: / 16-09-2011 / 14:52:08 / cg"
     "Modified: / 18-09-2013 / 16:47:13 / Jan Vrany <jan.vrany@fit.cvut.cz>"
+    "Modified (comment): / 01-05-2016 / 10:05:10 / cg"
 !
 
 findNodeForInterval:interval inParseTree:parseTree
@@ -3960,6 +4141,33 @@
     "Modified: / 28-08-2013 / 15:28:01 / cg"
 !
 
+sortUsefulSelectorsIn:selectorList
+    "/ cosmetics: 
+    "/  ifTrue / whileTrue should come before ifFalse/whileFalse
+    #(
+        ifTrue:         ifFalse:
+        ifTrue:ifFalse: ifFalse:ifTrue:
+        whileTrue:      whileFalse:
+        whileTrue       whileFalse
+        whileTrue:      whileTrue
+        whileFalse:     whileFalse
+        new:            basicNew:
+        new             basicNew
+    ) pairWiseDo:[:sel1 :sel2 |
+        |idx1 idx2|
+
+        (idx1 := selectorList indexOf:sel1) ~~ 0 ifTrue:[
+            (idx2 := selectorList indexOf:sel2) ~~ 0 ifTrue:[
+                idx1 > idx2 ifTrue:[ 
+                    selectorList swap:idx1 with:idx2
+                ] 
+            ] 
+        ].
+    ].
+
+    "Created: / 01-05-2016 / 17:48:02 / cg"
+!
+
 treeForCode:source allowErrors:allowErrors
     |tree|
 
@@ -4000,7 +4208,7 @@
      legal, but stupid message send to be parsed...
      (which happens often after inserting)"
 
-    |node nodeParent checkedNode characterBeforeCursor nodeIsInTemporaries|
+    |node nodeParent checkedNode characterBeforeCursor|
 
     "/ this is too naive and stupid; if there is a syntactic error,
     "/ we will not find a node for a long time (stepping back more and more,
@@ -4011,7 +4219,7 @@
     "/ that will also work for syntactic incorrect source code.
     (mustBeExpression not and:[methodOrNil notNil or:[classOrNil notNil]]) ifTrue:[
         node := self findNodeForInterval:interval in:source allowErrors:true mustBeMethod:true.
-    ].
+    ].           
     node isNil ifTrue:[
         node := self findNodeForInterval:interval in:source allowErrors:true mustBeMethod:false mustBeExpression:true.
         node isNil ifTrue:[
@@ -4033,25 +4241,47 @@
     characterBeforeCursor := source at:(characterPositionOfCursor-1 max:1). "/ codeView characterBeforeCursor.
     characterBeforeCursor isNil ifTrue:[ "at begin of line" ^ self].
     characterBeforeCursor == $. ifTrue:[ "at end of statement" ^ self].
-
+ 
     node isVariable ifTrue:[
-        |classes cls|
-        
+        |nodeIsInTemporaries nodeIsInBlockArguments nodeIsInMethodArguments |
+
         nodeIsInTemporaries :=
             nodeParent notNil
             and:[ nodeParent isSequence
             and:[ nodeParent temporaries notEmptyOrNil
-            and:[ node stop <= nodeParent temporaries last stop ]]].
-
-        nodeIsInTemporaries ifFalse:[
-            "/ cursor must be right after the variable
-            codeView characterPositionOfCursor = (node stop + 1) ifTrue:[
+            and:[ node stop <= nodeParent temporaries last stop ]]]. 
+
+        nodeIsInBlockArguments :=
+            node blockScope notNil
+            and:[ node blockScope arguments notEmptyOrNil
+            and:[ node stop <= node blockScope arguments last stop ]].
+
+        (nodeIsInBlockArguments not and:[rememberedScopeNodes notNil]) ifTrue:[
+            "/ sigh - parent (and therefore blockScope) is unknown if parser has error
+            nodeIsInBlockArguments := 
+                rememberedScopeNodes 
+                    contains:[:scope |
+                        (scope isMethod or:[scope isBlock])
+                        and:[scope arguments notEmpty
+                        and:[scope arguments first start <= node start
+                        and:[scope arguments last stop >= node stop]]].
+                    ].        
+        ].
+        nodeIsInTemporaries ifTrue:[ ^ self ]. "/ no completion in a tempvar decl 
+        nodeIsInBlockArguments ifTrue:[ ^ self ]. "/ no completion in a tempvar decl 
+
+        "/ for variable completion, cursor must be right after the node 
+        codeView characterPositionOfCursor = (node stop + 1) ifTrue:[
+            codeView characterBeforeCursor ~= Character space ifTrue:[
                 self codeCompletionForVariable:node into:actionBlock.
                 ^ self.
-            ]
-        ].
+            ]. 
+        ]. 
+    ].
+
 false ifTrue:[
         codeView characterPositionOfCursor = (node stop + 2) ifTrue:[
+            |classes cls| 
             "/ after a variable;
             "/ offer local messages, if receiver type is known
             classes := (self classesOfNode:node).
@@ -4081,8 +4311,6 @@
             ]
         ]
 ].
-^ self
-    ].
 
     node isLiteral ifTrue:[
         "/ however, user may want to complete a symbol inside a literal array!!
@@ -4131,32 +4359,141 @@
         nodeParent := node parent.
     ].
 
+
+    "/ Transcript show:'node is ';showCR:node.
+
     "/ move outward, until we find a message-send node,
     "/ or the method's selector pattern node.
     checkedNode := node.
     [checkedNode notNil] whileTrue:[
         (characterPositionOfCursor < (checkedNode stop ? source size)) ifTrue:[
-            self information:'Inside a message node'.
-            ^ self.
+Transcript show:'T: '; showCR:node.
+            "/ Transcript showCR:('Inside a ',(checkedNode className)).
+            self information:('Inside a ',(checkedNode className)).
+            (node isVariable or:[node isBlock and:[node stop notNil]]) ifTrue:[
+                characterPositionOfCursor == (node stop + 1) ifTrue:[
+                    codeView characterBeforeCursor == Character space ifTrue:[
+                        self codeCompletionForMessageTo:node into:actionBlock.
+                        ^ self
+                    ].
+                ].
+                characterPositionOfCursor == (node stop) ifTrue:[
+                    "/ hack (spaces at end of line)
+                    codeView characterBeforeCursor == Character space ifTrue:[
+                        self codeCompletionForMessageTo:node into:actionBlock.
+                        ^ self
+                    ]
+                ].
+            ].
+
+            (checkedNode isMessage 
+            and:[characterPositionOfCursor < (checkedNode selectorParts first start)]) ifTrue:[
+                self codeCompletionForMessageTo:checkedNode receiver into:actionBlock.
+                ^ self
+            ]
+            
         ].
 
         checkedNode isMessage ifTrue:[
             "/ completion in a message-send
+            "/ Transcript showCR:'codeCompletionForMessage'.
             self codeCompletionForMessage:checkedNode into:actionBlock.
             ^ self
         ].
         checkedNode isMethod ifTrue:[
             "/ completion in a method's selector pattern
+            "/ Transcript showCR:'codeCompletionForMethodSpec'.
             self codeCompletionForMethodSpec:checkedNode into:actionBlock.
             ^ self.
         ].
+
+
         checkedNode := checkedNode parent.
     ].
 
+    "/ Transcript showCR:'Node is neither variable nor message'.
     self information:'Node is neither variable nor message.'.
 
     "Modified: / 04-07-2006 / 18:48:26 / fm"
-    "Modified: / 16-09-2011 / 14:54:47 / cg"
+    "Modified: / 01-05-2016 / 17:55:11 / cg"
+!
+
+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."
+
+    |nodeVal con privateClass pool|
+
+    aVariableName isUppercaseFirst ifTrue:[
+        classOrNil notNil ifTrue:[
+            (classOrNil theNonMetaclass classVarNames includes:aVariableName) ifTrue:[
+                nodeVal := classOrNil theNonMetaclass classVarAt:aVariableName.
+                ^ { nodeVal . #classVariable }
+            ].
+            privateClass := classOrNil theNonMetaclass privateClasses detect:[:cls | cls nameWithoutPrefix = aVariableName] ifNone:nil.
+            privateClass notNil ifTrue:[
+                nodeVal := privateClass.
+                ^ { nodeVal . #privateClass }
+            ].
+            pool := classOrNil theNonMetaclass sharedPools detect:[:pool | pool classVariableNames includes:aVariableName] ifNone:nil.
+            pool notNil ifTrue:[
+                nodeVal := pool classVarAt:aVariableName.
+                ^ { nodeVal . #poolVariable }
+            ].
+        ].
+        aVariableName knownAsSymbol ifTrue:[
+            nodeVal := Smalltalk at:aVariableName asSymbol.
+            nodeVal notNil ifTrue:[     
+                ^ { nodeVal . #global }
+            ]
+        ].
+
+        "/ 'evaluate' the variable (like in a browser's codeView)
+        "/ mhmh - will we catch workspace vars then?
+        Error handle:[:ex |
+        ] do:[
+            nodeVal := Parser new evaluate:aVariableName in:classOrNil receiver:classOrNil.
+        ].
+        nodeVal notNil ifTrue:[
+            ^ { nodeVal . #global }
+        ].
+        ^ nil
+    ].
+
+    aVariableName = 'self' ifTrue:[
+        (classOrNil notNil and:[classOrNil isMeta]) ifTrue:[
+            ^ { classOrNil theNonMetaclass . #pseudoVar }
+        ].
+        contextOrNil notNil ifTrue:[
+            ^ { contextOrNil receiver . #pseudoVar } 
+        ].
+        ^ nil
+    ].
+
+    contextOrNil notNil ifTrue:[
+        con := contextOrNil.
+        [ con notNil ] whileTrue:[
+            "/ a local in the context?
+            ((con argAndVarNames ? #()) includes:aVariableName) ifTrue:[
+                nodeVal := con argsAndVars at:(con argAndVarNames indexOf:aVariableName) ifAbsent:nil.
+                nodeVal notNil ifTrue:[
+                    ^ { nodeVal . #argument }
+                ].
+            ].
+            con := con home.
+        ].
+        "/ an instvar
+        (contextOrNil receiver class allInstVarNames includes:aVariableName) ifTrue:[
+            nodeVal := contextOrNil receiver instVarNamed:aVariableName.
+            nodeVal notNil ifTrue:[
+                ^ { nodeVal . #instanceVariable }
+            ].
+        ].
+    ].
+    ^ nil
+
+    "Created: / 01-05-2016 / 12:40:05 / cg"
 !
 
 valueOfNode:aNode
@@ -4213,58 +4550,15 @@
      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."
 
-    |nodeVal con privateClass|
-
-    aVariableName isUppercaseFirst ifTrue:[
-        "/ simply 'evaluate' the variable (like in a browser's codeView)
-        "/ mhmh - will we catch workspace vars then?
-        Error handle:[:ex |
-        ] do:[
-            nodeVal := Parser new evaluate:aVariableName in:nil receiver:classOrNil.
-        ].
-        nodeVal notNil ifTrue:[
-            ^ nodeVal
-        ].
-        classOrNil notNil ifTrue:[
-            (classOrNil theNonMetaclass classVarNames includes:aVariableName) ifTrue:[
-                nodeVal := classOrNil theNonMetaclass classVarAt:aVariableName.
-                ^ nodeVal.
-            ].
-            privateClass := classOrNil theNonMetaclass privateClasses detect:[:cls | cls nameWithoutPrefix = aVariableName] ifNone:nil.
-            privateClass notNil ifTrue:[
-                nodeVal := privateClass.
-                ^ nodeVal.
-            ].
-        ].
-        ^ nil
-    ].
-    aVariableName = 'self' ifTrue:[
-        (classOrNil notNil and:[classOrNil isMeta]) ifTrue:[^ classOrNil theNonMetaclass].
-        contextOrNil notNil ifTrue:[^ contextOrNil receiver].
-        ^ nil
-    ].
-
-    contextOrNil notNil ifTrue:[
-        con := contextOrNil.
-        [ con notNil ] whileTrue:[
-            "/ a local in the context?
-            ((con argAndVarNames ? #()) includes:aVariableName) ifTrue:[
-                nodeVal := con argsAndVars at:(con argAndVarNames indexOf:aVariableName) ifAbsent:nil.
-                nodeVal notNil ifTrue:[
-                    ^ nodeVal
-                ].
-            ].
-            con := con home.
-        ].
-        "/ an instvar
-        (contextOrNil receiver class allInstVarNames includes:aVariableName) ifTrue:[
-            nodeVal := contextOrNil receiver instVarNamed:aVariableName.
-            nodeVal notNil ifTrue:[
-                ^ nodeVal
-            ].
-        ].
+    |valueAndKind|
+
+    (valueAndKind := self valueAndKindOfVariable:aVariableName) notNil ifTrue:[
+        self assert:valueAndKind isArray.
+        ^ valueAndKind first.
     ].
     ^ nil
+
+    "Modified: / 01-05-2016 / 12:41:30 / cg"
 !
 
 withoutSelectorsUnlikelyFor:aClass from:selectorsArg forPartial:partialSelector