#UI_ENHANCEMENT by cg
class: DoWhatIMeanSupport
added:
#classesOfInstVarNamed:inClass:
#classesOfNode:
changed:
#classOfNode:
#codeCompletionForMessage:inClass:instance:context:codeView:
#codeCompletionForMessage:into:
#tryCodeCompletionWithSource:nodeInterval:at:mustBeExpression:into:
--- 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.
].