DoWhatIMeanSupport.st
changeset 5043 721c2ef8d1bb
parent 5042 151b3c1bf4d1
child 5044 f8c330427edc
--- a/DoWhatIMeanSupport.st	Sun May 01 22:09:43 2016 +0200
+++ b/DoWhatIMeanSupport.st	Mon May 02 14:10:20 2016 +0200
@@ -1,5 +1,3 @@
-"{ Encoding: utf8 }"
-
 "
  COPYRIGHT (c) 2002 by eXept Software AG
 	      All Rights Reserved
@@ -1214,7 +1212,7 @@
 !
 
 codeCompletionForLanguage: languageOrNil class: classOrNilArg context:contextOrNilArg codeView:codeViewArg
-    "OBSOLETE; migrating to use the the new 'xxx: into:' protocol.
+    "going to become OBSOLETE; migrating to use the the new 'xxx: into:' protocol.
      contextOrNil is the current context, if this is called from the debugger;
      nil, if called from the browser.
      If nonNil, we can make better guesses, 
@@ -1621,6 +1619,46 @@
 
 !DoWhatIMeanSupport methodsFor:'code completion-helpers'!
 
+addClassesOfExpression:expr inClass:aClass to:setOfTypes
+    |cls exprSelector val|
+    
+    expr isConstant ifTrue:[
+        val := expr evaluate.
+        cls := val class.         
+        (val isArray or:[ val isByteArray or:[ val isString ]]) ifTrue:[
+            val isImmutable ifTrue:[
+                setOfTypes add:cls mutableClass.
+                ^ self.    
+            ]
+        ].
+        setOfTypes add:cls.
+        ^ self.    
+    ].
+    
+    expr isMessage ifTrue:[
+        exprSelector := expr selector. 
+        ( #(+ - * /) includes:exprSelector ) ifTrue:[
+            setOfTypes add:Number.
+            ^ self.
+        ].    
+        ( #(// size) includes:exprSelector ) ifTrue:[
+            setOfTypes add:Integer.
+            ^ self.
+        ].    
+        ( #(copy shallowCopy) includes:exprSelector ) ifTrue:[
+            "/ self addClassesOfExpression:expression receiver inClass:aClass to:setOfTypes
+            ^ self.
+        ].
+        ( #(new new: basicNew basicNew:) includes:exprSelector ) ifTrue:[
+            expr receiver isGlobal ifTrue:[
+                setOfTypes add:expr receiver evaluate.
+                ^ self.
+            ].    
+        ].   
+self breakPoint:#cg.
+    ].
+!
+
 askUserForCompletion:what for:codeView at:position from:allTheBest
     |list choice lastChoice|
 
@@ -1724,6 +1762,25 @@
     ^ nil
 !
 
+classesFromAssignmentTo:varName in:aTree
+    |classesFromAssignments|
+    
+    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|
     
@@ -1751,30 +1808,32 @@
                     visitor 
                         actionForNodeClass:AssignmentNode 
                         put:[:node |
-                            |val|
+                            |val expr exprSelector|
 
                             node variable name = varName ifTrue:[
+                                expr := node expression.
                                 "/ only look for wellknown types on the right side.
-                                node expression isConstant ifTrue:[
-                                    val := node expression evaluate.
+                                expr isConstant ifTrue:[
+                                    val := expr evaluate.
                                     val isArray ifTrue:[
                                         setOfTypes add:Array 
                                     ] ifFalse:[
                                         setOfTypes add:val class
                                     ].
                                 ] ifFalse:[
-                                    node expression isMessage ifTrue:[
-                                        ( #(+ - * /) includes:node expression selector ) ifTrue:[
+                                    expr isMessage ifTrue:[
+                                        exprSelector := expr selector. 
+                                        ( #(+ - * /) includes:exprSelector ) ifTrue:[
                                             setOfTypes add:Number
                                         ] ifFalse:[    
-                                            ( #(// size) includes:node expression selector ) ifTrue:[
+                                            ( #(// size) includes:exprSelector ) ifTrue:[
                                                 setOfTypes add:Integer
                                             ] ifFalse:[    
-                                                ( #(copy shallowCopy) includes:node expression selector ) ifTrue:[
+                                                ( #(copy shallowCopy) includes:exprSelector ) ifTrue:[
                                                 ] ifFalse:[    
-                                                    ( #(new new: basicNew basicNew:) includes:node expression selector ) ifTrue:[
-                                                        node expression receiver isGlobal ifTrue:[
-                                                            setOfTypes add:node expression receiver evaluate
+                                                    ( #(new new: basicNew basicNew:) includes:exprSelector ) ifTrue:[
+                                                        expr receiver isGlobal ifTrue:[
+                                                            setOfTypes add:expr receiver evaluate
                                                         ].    
                                                     ] ifFalse:[    
 self breakPoint:#cg.
@@ -2063,7 +2122,8 @@
      receiverNodeClassIfKnown 
      offerParenthisationAroundNode parenthesisAroundIndex
      parentNodeToParenthesize 
-     classesFromAssignmentsToReceiver otherMessagesToReceiver|
+     classesFromAssignmentsToReceiver otherMessagesToReceiver
+     canParenthesize|
  
     "/ Transcript show:'node '; show:node; show:' ; '.
     "/ Transcript show:'msg in '; show:methodOrNil; show:' / '; showCR:classOrNil.
@@ -2131,7 +2191,7 @@
     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)
@@ -2161,43 +2221,12 @@
  
         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
-                ]
-            ]
-        ].
+        classesFromAssignmentsToReceiver := self classesFromAssignmentTo:receiverName in:tree.
+
         possibleClasses := classesFromAssignmentsToReceiver.
- 
         possibleClasses isEmpty ifTrue:[
             "/ messages sent
-            otherMessagesToReceiver := Set new.
-            rememberedNodes notNil ifTrue:[
-                rememberedNodes do:[:node | 
-                    (node isMessage 
-                    and:[node receiver isVariable
-                    and:[node receiver name = nodeReceiver name]]) ifTrue:[
-                        selector ~= node selector ifTrue:[ 
-                            otherMessagesToReceiver add:(node selector)
-                        ].
-                    ].
-                ].
-            ] ifFalse:[
-                tree allMessageNodesDo:[:eachMessageNode |
-                    (nodeReceiver = eachMessageNode receiver
-                        and:[ selector ~= eachMessageNode selector]
-                    ) ifTrue:[   
-                        otherMessagesToReceiver add:eachMessageNode selector
-                    ]
-                ].
-            ]. 
+            otherMessagesToReceiver := self messagesSentTo:receiverName in:tree.
             otherMessagesToReceiver remove:selector ifAbsent:[].
 
             otherMessagesToReceiver notEmpty ifTrue:[
@@ -2287,7 +2316,7 @@
     ] 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 isMessage and:[parentNode selector isKeywordSelector ]]) ifTrue:[
+        (parentNode notNil and:[ parentNode isKeywordMessage ]) ifTrue:[
             bestSelectors := bestSelectors select:[:sel | sel isUnarySelector ]
         ]
     ].
@@ -2296,12 +2325,12 @@
 "/    bestSelectors sort:[:a :b | a size < b size].
  
     (selector isUnarySelector and:[ parentNode notNil and:[ parentNode isMessage ]]) ifTrue:[
-        (selector2 := parentNode selector) isKeywordSelector ifTrue:[
+        (selector2 := parentNode selector) isKeyword ifTrue:[
             "/ if its a unary message AND the parent is a keyword node, look for parent completion too.
             "/ i.e. look if there is a longer keyword possible
             selector2 := selector2,selector.
             bestSelectors2 := findBest value:(parentNode receiver) value:selector2.
-            bestSelectors2 := bestSelectors2 select:[:sel | sel isKeywordSelector and:[ sel startsWith:selector2]].
+            bestSelectors2 := bestSelectors2 select:[:sel | sel isKeyword and:[ sel startsWith:selector2]].
             bestSelectors2 := bestSelectors2 asOrderedCollection sort:[:a :b | a size < b size].
             bestSelectors := bestSelectors reject:[:sel | bestSelectors2 includes:sel].
  
@@ -2338,7 +2367,7 @@
             "/           arg
  
             kwSels := findBest value:parentNode value:selector.
-            kwSels := kwSels select:[:sel | sel isKeywordSelector].
+            kwSels := kwSels select:[:sel | sel isKeyword].
  
             kwSels := kwSels asOrderedCollection sort:[:a :b | a size < b size].
  
@@ -2380,11 +2409,27 @@
                                 forPartial:selector.
         ].
     ].
-    (parentNode notNil 
-        and:[ parentNode isMessage
-        and:[ ((parentNode selector isUnarySelector not) and:[selector isUnarySelector])
-          or:[ ((parentNode selector isKeywordSelector) and:[selector isBinarySelector]) ]]]
-    ) ifTrue:[
+    
+    Transcript show:'parentNode: '; showCR:parentNode.
+    Transcript show:'parentNode: '; showCR:parentNode class.
+    Transcript show:'sel: '; showCR:selector.
+    
+    canParenthesize := false.
+    parentNode notNil ifTrue:[
+        parentNode isMessage ifTrue:[
+            (((parentNode selector isUnarySelector not) and:[selector isUnarySelector])
+            or:[ ((parentNode selector isKeyword) and:[selector isBinarySelector]) ]) ifTrue:[
+                canParenthesize := true.
+            ]
+        ] ifFalse:[
+            offerParenthisationAroundNode isNil ifTrue:[
+                selector isKeyword ifTrue:[
+                    offerParenthisationAroundNode := node.
+                ].    
+            ].    
+        ].    
+    ].    
+    canParenthesize 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
@@ -2436,33 +2481,7 @@
     ].
  
     allBest := (bestSelectors ? #()) , (bestSelectors2 ? #()).
-    allBest sort:
-        [:a :b |
-            |aBeforeB|
-            
-            (a startsWith:selector) ifTrue:[
-                (b startsWith:selector) ifFalse:[
-                    aBeforeB := true
-                ]
-            ] ifFalse:[    
-                (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
-        ].
+    self sortSelectors:allBest forSelector:selector lcSelector:lcSelector.
                         
     split :=
         [:list :splitHow |
@@ -2847,6 +2866,7 @@
         suggestions := selectorsImplementedInClass asNewOrderedCollection sort.
     ].
 
+    suggestions := suggestions reject:[:sel | sel first == $_].
     self sortUsefulSelectorsIn:suggestions. "/cosmetics
 
     pos := codeView characterPositionOfCursor.
@@ -3164,7 +3184,6 @@
             selectors0 notEmptyOrNil ifTrue:[
                 selectors := selectors0,selectors.
             ].
-
             editAction :=
                 [:selectedCompletionIndex |
 
@@ -3409,7 +3428,11 @@
                                 ifTrue:[ otherArgNames addAll:(parseTree arguments collect:[:each | each name])] ].
             addWithFactorBlock value:otherArgNames value:(1.5 * localFactor).
         ].
-        addWithFactorBlock value:(codeView previousReplacements collect:[:p | p value asString]) value:(1.3 * localFactor).
+        addWithFactorBlock 
+            value:(codeView previousReplacements 
+                                    collect:[:p | p value asString]
+                                    thenSelect:[:s | s isValidSmalltalkIdentifier]) 
+            value:(1.3 * localFactor).
     ] ifFalse:[
         "/ locals in the block/method
         |names nameSpace|
@@ -4125,6 +4148,32 @@
     ].
 !
 
+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 | 
+            (node isMessage 
+            and:[node receiver isVariable
+            and:[node receiver name = varName]]) ifTrue:[
+                messagesToReceiver add:(node selector)
+            ].
+        ].
+    ] ifFalse:[
+        tree allMessageNodesDo:[:node |
+            (node isMessage 
+            and:[node receiver isVariable
+            and:[node receiver name = varName]]) ifTrue:[
+                messagesToReceiver add:node selector
+            ]
+        ].
+    ]. 
+    ^ messagesToReceiver
+!
+
 old_askUserForCompletion:what for:codeView from:allTheBest
     |list resources choice lastChoice|
 
@@ -4188,6 +4237,35 @@
     "Modified: / 28-08-2013 / 15:28:01 / cg"
 !
 
+sortSelectors:list forSelector:selector lcSelector:lcSelector
+    list sort: [:a :b |
+        |aBeforeB|
+
+        (a startsWith:selector) ifTrue:[
+            (b startsWith:selector) ifFalse:[
+                aBeforeB := true
+            ]
+        ] ifFalse:[    
+            (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
+    ].
+!
+
 sortUsefulSelectorsIn:selectorList
     "/ cosmetics: 
     "/  ifTrue / whileTrue should come before ifFalse/whileFalse
@@ -4323,7 +4401,8 @@
                 self codeCompletionForVariable:node into:actionBlock.
                 ^ self.
             ]. 
-        ]. 
+        ].
+        "/ nodeParent isNil or:[nodeParent isMessage not]
     ].
 
 false ifTrue:[
@@ -4407,32 +4486,29 @@
     ].
 
 
-    "/ Transcript show:'node is ';showCR:node.
+    Transcript show:'node is '.
+    Error ignoreIn:[ Transcript show:node ].
+    Transcript cr.
+
+    (node isVariable or:[node isBlock and:[node stop notNil]]) ifTrue:[
+        (characterPositionOfCursor == (node stop + 1)
+        "/ hack (spaces at end of line)
+        or:[characterPositionOfCursor == (node stop)]) ifTrue:[
+            codeView characterBeforeCursor == Character space ifTrue:[
+                self codeCompletionForMessageTo:node into:actionBlock.
+                ^ self
+            ].
+        ].
+    ].
 
     "/ 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:[
+        (characterPositionOfCursor <= (checkedNode stop ? source size)) ifTrue:[
             "/ 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
-                    ]
-                ].
-            ].
-
+            Transcript showCR:('Inside a ',(checkedNode className)).
+            "/ self information:('Inside a ',(checkedNode className)).
             (checkedNode isMessage 
             and:[characterPositionOfCursor < (checkedNode selectorParts first start)]) ifTrue:[
                 self codeCompletionForMessageTo:checkedNode receiver into:actionBlock.