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