#UI_ENHANCEMENT by cg
authorClaus Gittinger <cg@exept.de>
Fri, 29 Apr 2016 09:58:36 +0200
changeset 5035 5afe663f6f8d
parent 5033 743f882894d9
child 5036 d3bf0720d6b1
#UI_ENHANCEMENT by cg class: DoWhatIMeanSupport added: #classesOfInstVarNamed:inClass: #classesOfNode: changed: #classOfNode: #codeCompletionForMessage:inClass:instance:context:codeView: #codeCompletionForMessage:into: #tryCodeCompletionWithSource:nodeInterval:at:mustBeExpression:into:
DoWhatIMeanSupport.st
--- a/DoWhatIMeanSupport.st	Thu Apr 28 15:42:57 2016 +0200
+++ b/DoWhatIMeanSupport.st	Fri Apr 29 09:58:36 2016 +0200
@@ -1703,40 +1703,131 @@
 !
 
 classOfNode:aNode
-    "when showing possible completions for a message,
+    "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."
 
-    | nm nodeVal receiverClass nodeSelector nodeReceiver mthd|
+    | classes |
+
+    classes := self classesOfNode:aNode.
+    classes size == 1 ifTrue:[
+        ^ classes anElement
+    ].
+    ^ nil
+!
+
+classesOfInstVarNamed:varName inClass:aClass
+    |setOfTypes instIndex|
+    
+    setOfTypes := IdentitySet new.
+    instIndex := aClass instVarIndexFor:varName.
+
+    "/ look for instances
+    aClass allSubInstancesDo:[:i |
+        |varClass|
+        varClass := (i instVarAt:instIndex) class.
+        setOfTypes add:varClass.
+    ].  
+    
+    "/ look for assignments in code
+    aClass withAllSubclassesDo:[:eachClass |
+        eachClass methodDictionary do:[:m |
+            |tree code visitor|
+
+            "/ quick check
+            code := m source.
+            (code notNil and:[code includesString:varName]) ifTrue:[
+                tree := Parser parse:code class:eachClass.
+                (tree notNil and:[tree ~~ #Error]) ifTrue:[
+                    visitor := PluggableParseNodeVisitor new. 
+                    visitor 
+                        actionForNodeClass:AssignmentNode 
+                        put:[:node |
+                            |val|
+
+                            node variable name = varName ifTrue:[
+                                "/ only look for wellknown types on the right side.
+                                node expression isConstant ifTrue:[
+                                    val := node expression evaluate.
+                                    val isArray ifTrue:[
+                                        setOfTypes add:Array 
+                                    ] ifFalse:[
+                                        setOfTypes add:val class
+                                    ].
+                                ] ifFalse:[
+                                    node expression isMessage ifTrue:[
+                                        ( #(+ - * /) includes:node expression selector ) ifTrue:[
+                                            setOfTypes add:Number
+                                        ] ifFalse:[    
+                                            ( #(// size) includes:node expression selector ) ifTrue:[
+                                                setOfTypes add:Integer
+                                            ] ifFalse:[    
+                                                ( #(copy shallowCopy) includes:node expression selector ) ifTrue:[
+                                                ] ifFalse:[    
+                                                    ( #(new new: basicNew basicNew:) includes:node expression selector ) ifTrue:[
+                                                        node expression receiver isGlobal ifTrue:[
+                                                            setOfTypes add:node expression receiver evaluate
+                                                        ].    
+                                                    ] ifFalse:[    
+self breakPoint:#cg.
+                                                    ]
+                                                ]
+                                            ]
+                                        ]
+                                    ].    
+                                ].    
+                            ].
+                            true "/ yes - visit subnodes
+                        ].        
+                    visitor visit:tree.
+                ].    
+            ]    
+        ]
+    ].
+    ^ setOfTypes
+!
+
+classesOfNode:aNode
+    "returns the set of possible classes of a receiver.
+     or nil if unknown.
+     When showing possible completions for a message,
+     it is a good idea to know what the kind receiver is."
+
+    | nm nodeVal receiverClass nodeSelector nodeReceiver mthd instVarClass|
 
     aNode isBlock ifTrue:[
-        ^ Block
+        ^ { Block }
     ].
-
     (nodeVal := self valueOfNode:aNode) notNil ifTrue:[
         "/ knowing the value is always great!!
-        ^ nodeVal class
+        ^ { nodeVal class }
     ].
 
     aNode isVariable ifTrue:[
         nm := aNode name.
         nm = 'self' ifTrue:[
-            classOrNil isNil ifTrue:[^ UndefinedObject].
-            ^ classOrNil
+            classOrNil isNil ifTrue:[^ { UndefinedObject } ].
+            ^ { classOrNil }
         ].
         nm = 'super' ifTrue:[
             classOrNil isNil ifTrue:[^ Object].
-            ^ classOrNil superclass
+            ^ { classOrNil superclass }
         ].
         nm = 'thisContext' ifTrue:[
-            ^ Context
+            ^ { Context }
         ].
 
-"/        classOrNil notNil ifTrue:[
+        classOrNil notNil ifTrue:[
+            instVarClass := classOrNil whichClassDefinesInstVar:nm.
+            instVarClass notNil ifTrue:[
+                ^ self classesOfInstVarNamed:nm inClass:instVarClass.
+            ].    
 "/            (classOrNil allInstVarNames includes:nm) ifTrue:[
 "/                "/ could look at existing instances here...
 "/                self breakPoint:#cg.
 "/            ].
-"/        ].
+        ].
         ^ nil
     ].
 
@@ -1748,25 +1839,25 @@
         receiverClass := self classOfNode:nodeReceiver.
         receiverClass notNil ifTrue:[
             nodeSelector == #class ifTrue:[
-                ^ receiverClass class
+                ^ { receiverClass class }
             ].
 
             receiverClass isBehavior ifTrue:[
                 mthd := receiverClass lookupMethodFor:nodeSelector.
                 receiverClass isMeta ifTrue:[
                     ( #( #'new' #'basicNew' #'new:' #'basicNew:' #'with:' #'with:with:') includes: nodeSelector ) ifTrue:[
-                        ^ receiverClass theNonMetaclass
+                        ^ { receiverClass theNonMetaclass }
                     ].
                     "/ 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:[
-                            ^ receiverClass theNonMetaclass
+                            ^ { receiverClass theNonMetaclass }
                         ].
                     ].
                 ] ifFalse:[
                     mthd notNil ifTrue:[
                         (ParseTreeSearcher methodIsSetterMethod:mthd) ifTrue:[
-                            ^ receiverClass.
+                            ^ { receiverClass }.
                         ]
                     ]
                 ]
@@ -1774,24 +1865,24 @@
         ].
         classOrNil notNil ifTrue:[
             (nodeReceiver isSelf and:[nodeSelector = #'class']) ifTrue:[
-                ^ classOrNil class
+                ^ { classOrNil class }
             ].
         ].
 
         (nodeSelector = #'asFilename') ifTrue:[
-            ^ Filename
+            ^ { Filename }
         ].
         (nodeSelector = #'asOrderedCollection') ifTrue:[
-            ^ OrderedCollection
+            ^ { OrderedCollection }
         ].
         (nodeSelector = #'asArray') ifTrue:[
-            ^ Array
+            ^ { Array }
         ].
         (nodeSelector = #'asSet') ifTrue:[
-            ^ Set
+            ^ { Set }
         ].
         (nodeSelector = #'size') ifTrue:[
-            ^ SmallInteger
+            ^ { SmallInteger }
         ].
 
         "/ some wellknown boolean returners (need better type inference here)
@@ -1802,22 +1893,20 @@
             and: or:
             exists atEnd
         ) includes:nodeSelector ) ifTrue:[
-            ^ True "/ Boolean - not boolean; it does not contain the full protocol (would not find ifTrue:)
+            ^ { True } "/ Boolean - not boolean; it does not contain the full protocol (would not find ifTrue:)
         ].
 
         ( #( + - * / // \\ ) includes:nodeSelector) ifTrue:[
             "/ assume numeric
-            ^ Number
+            ^ { Number }
         ].
 
         ( #( class theMetaclass theNonMetaclass ) includes:nodeSelector) ifTrue:[
             "/ assume behavior
-            ^ Behavior
+            ^ { Behavior }
         ].
     ].
     ^ nil
-
-    "Created: / 28-08-2013 / 16:34:53 / cg"
 !
 
 codeCompletionForLiteralSymbol:nodeOrNil element:tokenOrNil considerAll:considerAll into:actionBlock
@@ -1958,12 +2047,12 @@
 
     findBest := 
         [:node :selector |
-            |srchClass srchClasses bestSelectors bestPrefixes
+            |srchClasses bestSelectors bestPrefixes
              allMessagesSentToVariable classesImplementingAllMessages|
 
-            srchClass := self classOfNode:node.
-
-            srchClass isNil ifTrue:[
+            srchClasses := self classesOfNode:node.
+
+            srchClasses isEmptyOrNil ifTrue:[
                 node isVariable ifTrue:[
                     allMessagesSentToVariable := Set new.
                     rememberedNodes do:[:eachNode |
@@ -1991,16 +2080,19 @@
                 ].
             ].
             bestSelectors := Set new.
-            srchClasses isEmptyOrNil ifTrue:[ srchClasses := Array with:srchClass ].
-            srchClasses do:[:srchClass |
-                |bestForThisClass|
-
-                bestForThisClass := Parser findBest:50 selectorsFor:selector in:srchClass forCompletion:true.
-                bestForThisClass := self
-                                    withoutSelectorsUnlikelyFor:srchClass
-                                    from:bestForThisClass
-                                    forPartial:selector.
-                bestSelectors addAll:bestForThisClass.
+            srchClasses isEmptyOrNil ifTrue:[
+                bestSelectors addAll:( Parser findBest:50 selectorsFor:selector in:nil forCompletion:true ).
+            ] ifFalse:[    
+                srchClasses do:[:srchClass |
+                    |bestForThisClass|
+
+                    bestForThisClass := Parser findBest:50 selectorsFor:selector in:srchClass forCompletion:true.
+                    bestForThisClass := self
+                                        withoutSelectorsUnlikelyFor:srchClass
+                                        from:bestForThisClass
+                                        forPartial:selector.
+                    bestSelectors addAll:bestForThisClass.
+                ].
             ].
             (bestSelectors includes:selector) ifTrue:[
                 bestSelectors := bestSelectors select:[:sel | sel size > selector size].
@@ -2268,16 +2360,28 @@
     allBest sort:
         [:a :b |
             |aBeforeB|
-            aBeforeB := a < b.
-            (a asLowercase startsWith:lcSelector) ifTrue:[
-                (b asLowercase startsWith:lcSelector) ifFalse:[
+            
+            (a startsWith:selector) ifTrue:[
+                (b startsWith:selector) ifFalse:[
                     aBeforeB := true
                 ]
             ] ifFalse:[    
-                (b asLowercase startsWith:lcSelector) ifTrue:[
+                (b startsWith:selector) ifTrue:[
                     aBeforeB := false
                 ]
             ].
+            aBeforeB isNil ifTrue:[
+                aBeforeB := a asLowercase < b asLowercase.
+                (a asLowercase startsWith:lcSelector) ifTrue:[
+                    (b asLowercase startsWith:lcSelector) ifFalse:[
+                        aBeforeB := true
+                    ]
+                ] ifFalse:[    
+                    (b asLowercase startsWith:lcSelector) ifTrue:[
+                        aBeforeB := false
+                    ]
+                ].
+            ].
             aBeforeB
         ].
                         
@@ -3851,18 +3955,53 @@
     characterBeforeCursor == $. ifTrue:[ "at end of statement" ^ self].
 
     node isVariable ifTrue:[
+        |classes cls|
+        
         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
-            characterPositionOfCursor >= (node stop) ifTrue:[
+            codeView characterPositionOfCursor = (node stop + 1) ifTrue:[
                 self codeCompletionForVariable:node into:actionBlock.
+                ^ self.
             ]
         ].
-        ^ self.
+false ifTrue:[
+        codeView characterPositionOfCursor = (node stop + 2) ifTrue:[
+            "/ after a variable;
+            "/ offer local messages, if receiver type is known
+            classes := (self classesOfNode:node).
+            classes notEmptyOrNil ifTrue:[
+                classes size > 1 ifTrue:[
+                    cls := classes anElement.
+                ] ifFalse:[    
+                    cls := Behavior commonSuperclassOf:classes.
+                ]
+            ]. 
+            cls notNil ifTrue:[
+                |clsSelectors moreSelectors|
+            
+                "/ completion in a message-send
+                clsSelectors := cls methodDictionary keys. "/ Parser findBest:50 selectorsFor:'' in:cls forCompletion:true.
+                clsSelectors size < 30 ifTrue:[
+                    cls superclass notNil ifTrue:[
+                        moreSelectors := cls superclass methodDictionary keys.
+                        clsSelectors size + moreSelectors size < 30 ifTrue:[
+                            clsSelectors := clsSelectors , moreSelectors.
+                        ].
+                    ].    
+                ].
+                "/ self codeCompletionForMessage:checkedNode into:actionBlock.
+                actionBlock value:clsSelectors value:nil value:nil.
+                ^ self.
+            ]
+        ]
+].
+^ self
     ].
 
     node isLiteral ifTrue:[
@@ -4257,12 +4396,15 @@
     ].
 
     findBest := [:node :selector |
-        |srchClass bestSelectors bestPrefixes|
+        |srchClasses bestSelectors bestPrefixes|
 
         codeView withCursor:(Cursor questionMark) do:[
-            srchClass := self classOfNode:node receiver.
-            srchClass notNil ifTrue:[
-                bestSelectors := Parser findBest:30 selectorsFor:selector in:srchClass forCompletion:true.
+            srchClasses := self classesOfNode:node receiver.
+            srchClasses notNil ifTrue:[
+                bestSelectors := Set new.
+                srchClasses do:[:each |
+                    bestSelectors addAll:(Parser findBest:30 selectorsFor:selector in:each forCompletion:true).
+                ]    
             ] ifFalse:[
                 bestSelectors := Parser findBest:30 selectorsFor:selector in:nil forCompletion:true.
             ].