Skip to content

Commit

Permalink
More cleanups
Browse files Browse the repository at this point in the history
 - renamings
 - moving RubSelectorExtractor to a proper package
  • Loading branch information
guillep committed Nov 2, 2020
1 parent 82f68f6 commit 8699014
Show file tree
Hide file tree
Showing 2 changed files with 123 additions and 134 deletions.
134 changes: 0 additions & 134 deletions src/Rubric-Tests/RubSelectionExtractor.class.st

This file was deleted.

123 changes: 123 additions & 0 deletions src/Rubric/RubSelectorExtractor.class.st
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
]

0 comments on commit 8699014

Please sign in to comment.