Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Cursorless support for tree-sitter query .scm files #1448

Merged
merged 6 commits into from
Aug 22, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/common/src/extensionDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ export const extensionDependencies = [
"scalameta.metals",
"ms-python.python",
"mrob95.vscode-talonscript",
"jrieken.vscode-tree-sitter-query",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaning or more towards just including our own missing file extensions instead of having to add additional extension dependencies.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. Tho wrt .scm specifically, @auscompgeek makes a good point that we may want to avoid that one as it is more commonly used for Scheme. But for .talon and the others I think it makes sense for us to just register them

];
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ const testCases: TestCase[] = [
},

{
name: "should show error for capture with multiple start",
name: "should allow capture with multiple start",
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

captures: [
{
name: "@foo.start",
Expand All @@ -145,8 +145,8 @@ const testCases: TestCase[] = [
range: new Range(0, 2, 0, 3),
},
],
isValid: false,
expectedErrorMessageIds: ["TreeSitterQuery.checkCaptures.duplicate"],
isValid: true,
expectedErrorMessageIds: [],
},

{
Expand All @@ -157,7 +157,7 @@ const testCases: TestCase[] = [
range: new Range(0, 0, 0, 0),
},
{
name: "@foo.start",
name: "@foo",
range: new Range(0, 1, 0, 2),
},
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,16 +27,18 @@ export function checkCaptureStartEnd(

let shownError = false;

if (captures.length === 2) {
const startRange = captures.find(({ name }) => name.endsWith(".start"))
?.range;
const endRange = captures.find(({ name }) => name.endsWith(".end"))?.range;
if (startRange != null && endRange != null) {
if (startRange.end.isBeforeOrEqual(endRange.start)) {
// Found just a start and endpoint in the right order, so we're good
return true;
}

const lastStart = captures
.filter(({ name }) => name.endsWith(".start"))
.map(({ range: { end } }) => end)
.sort((a, b) => a.compareTo(b))
.at(-1);
const firstEnd = captures
.filter(({ name }) => name.endsWith(".end"))
.map(({ range: { start } }) => start)
.sort((a, b) => a.compareTo(b))
.at(0);
if (lastStart != null && firstEnd != null) {
if (lastStart.isAfter(firstEnd)) {
showError(
messages,
"TreeSitterQuery.checkCaptures.badOrder",
Expand All @@ -63,7 +65,7 @@ export function checkCaptureStartEnd(
shownError = true;
}

if (regularCount > 1 || startCount > 1 || endCount > 1) {
if (regularCount > 1) {
// Found duplicate captures
showError(
messages,
Expand All @@ -75,15 +77,5 @@ export function checkCaptureStartEnd(
shownError = true;
}

if (!shownError) {
// I don't think it's possible to get here, but just in case, show a generic
// error message
showError(
messages,
"TreeSitterQuery.checkCaptures.unexpected",
`Unexpected captures: ${captures}`,
);
}

return false;
return !shownError;
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,22 @@ class IsNthChild extends QueryPredicateOperator<IsNthChild> {
}
}

/**
* A predicate operator that returns true if the node has more than 1 child of
* type {@link type} (inclusive). For example, `(has-multiple-children-of-type?
* @foo bar)` will accept the match if the `@foo` capture has 2 or more children
* of type `bar`.
*/
class HasMultipleChildrenOfType extends QueryPredicateOperator<HasMultipleChildrenOfType> {
name = "has-multiple-children-of-type?" as const;
schema = z.tuple([q.node, q.string]);

run({ node }: MutableQueryCapture, type: string) {
const count = node.children.filter((n) => n.type === type).length;
return count > 1;
}
}

class ChildRange extends QueryPredicateOperator<ChildRange> {
name = "child-range!" as const;
schema = z.union([
Expand Down Expand Up @@ -170,4 +186,5 @@ export const queryPredicateOperators = [
new ShrinkToMatch(),
new AllowMultiple(),
new InsertionDelimiter(),
new HasMultipleChildrenOfType(),
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
languageId: scm
command:
version: 6
spokenForm: bring name to air
action:
name: replaceWithTarget
source:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
destination:
type: primitive
insertionMode: to
target:
type: primitive
mark: {type: decoratedSymbol, symbolColor: default, character: a}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
(aaa) @bbb @ccc @ddd
(eee) @fff
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
marks:
default.a:
start: {line: 0, character: 1}
end: {line: 0, character: 4}
finalState:
documentContents: |-
(aaa) @fff
(eee) @fff
selections:
- anchor: {line: 1, character: 0}
active: {line: 1, character: 0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
languageId: scm
command:
version: 6
spokenForm: change every name
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: everyScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: (aaa) @bbb @ccc @ddd
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: (aaa) @ @ @
selections:
- anchor: {line: 0, character: 7}
active: {line: 0, character: 7}
- anchor: {line: 0, character: 9}
active: {line: 0, character: 9}
- anchor: {line: 0, character: 11}
active: {line: 0, character: 11}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: scm
command:
version: 6
spokenForm: change name
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: (aaa) @bbb @ccc @ddd
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: (aaa) @
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

notice how "change name" targets all of them as one range. See #1631 (comment)

selections:
- anchor: {line: 0, character: 7}
active: {line: 0, character: 7}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: scm
command:
version: 6
spokenForm: change name
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: "eee: (aaa) @bbb @ccc @ddd"
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: "eee: (aaa) @"
selections:
- anchor: {line: 0, character: 12}
active: {line: 0, character: 12}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: scm
command:
version: 6
spokenForm: change name
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: "eee: _ @bbb @ccc @ddd"
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: "eee: _ @"
selections:
- anchor: {line: 0, character: 8}
active: {line: 0, character: 8}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
languageId: scm
command:
version: 6
spokenForm: change value
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: value}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
(
aaa: (bbb) @ccc @ddd
eee: "fff" @ggg
hhh: (iii)
jjj: [(kkk)] @lll
mmm: ((nnn) (ooo))* @ppp
qqq: _ @rrr
)
selections:
- anchor: {line: 1, character: 4}
active: {line: 1, character: 4}
- anchor: {line: 2, character: 4}
active: {line: 2, character: 4}
- anchor: {line: 3, character: 4}
active: {line: 3, character: 4}
- anchor: {line: 4, character: 4}
active: {line: 4, character: 4}
- anchor: {line: 5, character: 4}
active: {line: 5, character: 4}
- anchor: {line: 6, character: 4}
active: {line: 6, character: 4}
marks: {}
finalState:
documentContents: |-
(
aaa: @ccc @ddd
eee: @ggg
hhh:
jjj: @lll
mmm: @ppp
qqq: @rrr
)
selections:
- anchor: {line: 1, character: 9}
active: {line: 1, character: 9}
- anchor: {line: 2, character: 9}
active: {line: 2, character: 9}
- anchor: {line: 3, character: 9}
active: {line: 3, character: 9}
- anchor: {line: 4, character: 9}
active: {line: 4, character: 9}
- anchor: {line: 5, character: 9}
active: {line: 5, character: 9}
- anchor: {line: 6, character: 9}
active: {line: 6, character: 9}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
languageId: scm
command:
version: 6
spokenForm: chuck key
action:
name: remove
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: collectionKey}
usePrePhraseSnapshot: true
initialState:
documentContents: |-
(
aaa: (bbb) @ccc
)
selections:
- anchor: {line: 1, character: 19}
active: {line: 1, character: 19}
marks: {}
finalState:
documentContents: |-
(
(bbb) @ccc
)
selections:
- anchor: {line: 1, character: 14}
active: {line: 1, character: 14}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: scm
command:
version: 6
spokenForm: chuck name
action:
name: remove
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: (aaa) @bbb @ccc @ddd
selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
marks: {}
finalState:
documentContents: "(aaa) "
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't love this space, but I think it should probably be handled by #855

selections:
- anchor: {line: 0, character: 0}
active: {line: 0, character: 0}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
languageId: scm
command:
version: 6
spokenForm: chuck name
action:
name: remove
target:
type: primitive
modifiers:
- type: containingScope
scopeType: {type: name}
usePrePhraseSnapshot: true
initialState:
documentContents: (aaa) @bbb @ccc @ddd
selections:
- anchor: {line: 0, character: 20}
active: {line: 0, character: 20}
marks: {}
finalState:
documentContents: "(aaa) @bbb @ccc "
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above re space

selections:
- anchor: {line: 0, character: 16}
active: {line: 0, character: 16}
Loading