forked from pharo-project/pharo
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- renamings - moving RubSelectorExtractor to a proper package
- Loading branch information
Showing
2 changed files
with
123 additions
and
134 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,123 @@ | ||
" | ||
I implement a series of heuristics to extract selectors from source code. | ||
I have two main entry points: | ||
- `extractSelectorFromSelectedText: aText` receives some text and looks at it in isolation, regarless of the context around it, and tries to find a selector in it | ||
- `extractSelectorFromAST: anASTNode atPosition: aPositionInTheAST` gets the best node matching the position and extracts a selector from it. | ||
## Heuristics - position | ||
When extracting from a particular position, if the previous character is not a separator, we move the selection one position before. | ||
This heuristic is for the cases where the caret (|) is: | ||
|self foo => the caret is before self, do not move | ||
self foo| => the caret is before foo, interpret is as if we are in foo. | ||
self foo | => the caret is before a space, interpret is as if we are in foo. | ||
This heuristic introduces although an ambiguity when code is not nicely formatted: | ||
self foo:|#bar => Here a user may want foo: or bar. | ||
For now we decided to favor foo: to motivate people to indent code correctly | ||
## Heuristics - cascades | ||
When the best matching node is a message, a strange case may arrive with cascades. | ||
Cascade nodes contain messages and each message contains (duplicated) the receiver. | ||
So we need to deambiguate here: was the selection on the receiver? or on the message itself? | ||
For example: | ||
aVariable | ||
cascade1; | ||
cascade2. | ||
Selecting both `aVariable` or the message `cascade2` will yield a message node `aVariable cascade2`. | ||
However, in the first case, the cursor is closer to `cascade1`, so we want cascade1 while in the latter case we want cascade2. | ||
" | ||
Class { | ||
#name : #RubSelectorExtractor, | ||
#superclass : #Object, | ||
#category : #'Rubric-Editing-Code' | ||
} | ||
|
||
{ #category : #'public - extraction' } | ||
RubSelectorExtractor >> extractSelectorFromAST: ast atPosition: aPosition [ | ||
|
||
"Obtain the the best node matching the position and extracts a selector from it." | ||
|
||
"Position Heuristic: If the previous character is not a separator, take selection one position before. | ||
This heuristic is for the cases where the caret (|) is: | ||
|self foo => the caret is before self, do not move | ||
self foo| => the caret is before foo, interpret is as if we are in foo. | ||
self foo | => the caret is before a space, interpret is as if we are in foo. | ||
This heuristic introduces although an ambiguity when code is not nicely formatted: | ||
self foo:|#bar => Here a user may want foo: or bar. | ||
For now we decided to favor foo: to motivate people to indent code correctly" | ||
| offset position bestNodeAtPosition | | ||
offset := (ast sourceCode at: aPosition - 1) isSeparator | ||
ifTrue: [ 0 ] | ||
ifFalse: [ -1 ]. | ||
|
||
position := aPosition + offset. | ||
bestNodeAtPosition := ast bestNodeFor: (position to: position). | ||
^ self extractSelectorFromNode: bestNodeAtPosition | ||
] | ||
|
||
{ #category : #private } | ||
RubSelectorExtractor >> extractSelectorFromNode: aNode [ | ||
|
||
| node originalNode | | ||
originalNode := node := aNode. | ||
node isReturn ifTrue: [ | ||
node := node value ]. | ||
|
||
node isCascade ifTrue: [ | ||
^ node messages first selector ]. | ||
|
||
node isMethod ifFalse: [ | ||
(node isValue and: [ node value isSymbol ]) ifTrue: [ ^ node value ]. | ||
|
||
[ node isMessage or: [ node isMethod ] ] whileFalse: [ | ||
(node := node parent) ifNil: [ ^ nil ] ]. | ||
|
||
"This is a strange case with cascades. | ||
Cascade nodes contain messages. | ||
And each message contains (duplicated) the receiver. | ||
So we need to deambiguate here: | ||
was the selection on the receiver? or on the message itself? | ||
For example: | ||
aVariable | ||
cascade1; | ||
cascade2. | ||
Selecting both the receiver or the cascade2 will yield a message node `aVariable cascade2`. | ||
However, in the first case we want cascade1 and in the latter we want cascade2. | ||
" | ||
(node ~= originalNode | ||
and: [ node parent notNil | ||
and: [ node parent isCascade ] ]) | ||
ifTrue: [ node := node parent messages first ] ]. | ||
|
||
^node selector | ||
] | ||
|
||
{ #category : #'public - extraction' } | ||
RubSelectorExtractor >> extractSelectorFromSelection: aString [ | ||
|
||
"Extract a selector from the given string in isolation, regarless of the context around it" | ||
|
||
| node | | ||
node := RBParser parseFaultyExpression: aString. | ||
node | ||
nodesDo: [ :n | | ||
n isMessage | ||
ifTrue: [ ^ n selector ]. | ||
n isVariable | ||
ifTrue: [ ^ n name ]. | ||
n isLiteralNode | ||
ifTrue: [ ^ n value ] ]. | ||
|
||
"fall back" | ||
^ aString asSymbol | ||
] |