Skip to content

Commit 8039549

Browse files
Migrate clojure scope implementations (#2951)
1 parent 816f936 commit 8039549

File tree

8 files changed

+181
-169
lines changed

8 files changed

+181
-169
lines changed

packages/cursorless-engine/src/languages/LegacyLanguageId.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22
* The language IDs that we have full tree-sitter support for using our legacy
33
* modifiers.
44
*/
5-
export const legacyLanguageIds = ["clojure", "latex", "rust"] as const;
5+
export const legacyLanguageIds = ["latex", "rust"] as const;
66

77
export type LegacyLanguageId = (typeof legacyLanguageIds)[number];

packages/cursorless-engine/src/languages/TreeSitterQuery/QueryCapture.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,18 @@
11
import type { Range, TextDocument } from "@cursorless/common";
2-
import type { Point } from "web-tree-sitter";
2+
import type { Point, TreeCursor } from "web-tree-sitter";
33

44
/**
55
* Simple representation of the tree sitter syntax node. Used by
66
* {@link MutableQueryCapture} to avoid using range/text and other mutable
77
* parameters directly from the node.
88
*/
9-
interface SimpleSyntaxNode {
9+
export interface SimpleSyntaxNode {
1010
readonly id: number;
1111
readonly type: string;
1212
readonly isNamed: boolean;
1313
readonly parent: SimpleSyntaxNode | null;
1414
readonly children: Array<SimpleChildSyntaxNode>;
15+
walk(): TreeCursor;
1516
}
1617

1718
/**
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import type { SimpleSyntaxNode } from "./QueryCapture";
2+
3+
export function getChildNodesForFieldName(
4+
node: SimpleSyntaxNode,
5+
fieldName: string,
6+
): SimpleSyntaxNode[] {
7+
const nodes = [];
8+
const treeCursor = node.walk();
9+
let hasNext = treeCursor.gotoFirstChild();
10+
11+
while (hasNext) {
12+
if (treeCursor.currentFieldName === fieldName) {
13+
nodes.push(treeCursor.currentNode);
14+
}
15+
hasNext = treeCursor.gotoNextSibling();
16+
}
17+
18+
return nodes;
19+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import type { SimpleSyntaxNode } from "./QueryCapture";
2+
3+
/**
4+
* Checks if a node is at an even index within its parent's field.
5+
*
6+
* @param node - The node to check.
7+
* @param fieldName - The name of the field in the parent node.
8+
* @returns True if the node is at an even index, false otherwise.
9+
*/
10+
export function isEven(node: SimpleSyntaxNode, fieldName: string): boolean {
11+
if (node.parent == null) {
12+
throw Error("Node has no parent");
13+
}
14+
15+
const treeCursor = node.parent.walk();
16+
let hasNext = treeCursor.gotoFirstChild();
17+
let even = true;
18+
19+
while (hasNext) {
20+
if (treeCursor.currentFieldName === fieldName) {
21+
if (treeCursor.currentNode.id === node.id) {
22+
return even;
23+
}
24+
even = !even;
25+
}
26+
hasNext = treeCursor.gotoNextSibling();
27+
}
28+
29+
throw Error(`Node not found in parent for field: ${fieldName}`);
30+
}

packages/cursorless-engine/src/languages/TreeSitterQuery/queryPredicateOperators.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,49 @@ import { z } from "zod";
44
import { makeRangeFromPositions } from "../../util/nodeSelectors";
55
import type { MutableQueryCapture } from "./QueryCapture";
66
import { QueryPredicateOperator } from "./QueryPredicateOperator";
7+
import { isEven } from "./isEven";
78
import { q } from "./operatorArgumentSchemaTypes";
89

10+
/**
11+
* A predicate operator that returns true if the node is at an even index within
12+
* its parents field. For example, `(#even? @foo value)` will accept the match
13+
* if the `@foo` capture is at an even index among its parents value children.
14+
*/
15+
class Even extends QueryPredicateOperator<Even> {
16+
name = "even?" as const;
17+
schema = z.tuple([q.node, q.string]);
18+
run({ node }: MutableQueryCapture, fieldName: string) {
19+
return isEven(node, fieldName);
20+
}
21+
}
22+
23+
/**
24+
* A predicate operator that returns true if the node is at an odd index within
25+
* its parents field. For example, `(#odd? @foo value)` will accept the match
26+
* if the `@foo` capture is at an odd index among its parents value children.
27+
*/
28+
class Odd extends QueryPredicateOperator<Odd> {
29+
name = "odd?" as const;
30+
schema = z.tuple([q.node, q.string]);
31+
run({ node }: MutableQueryCapture, fieldName: string) {
32+
return !isEven(node, fieldName);
33+
}
34+
}
35+
36+
/**
37+
* A predicate operator that returns true if the node matches the given text.
38+
* For example, `(#text? @foo bar)` will accept the match if the `@foo`
39+
* captures text is `bar`. It is acceptable to pass in multiple texts, e.g.
40+
* `(#text? @foo bar baz)`.
41+
*/
42+
class Text extends QueryPredicateOperator<Text> {
43+
name = "text?" as const;
44+
schema = z.tuple([q.node, q.string]).rest(q.string);
45+
run({ document, range }: MutableQueryCapture, ...texts: string[]) {
46+
return texts.includes(document.getText(range));
47+
}
48+
}
49+
950
/**
1051
* A predicate operator that returns true if the node is of the given type.
1152
* For example, `(#type? @foo string)` will accept the match if the `@foo`
@@ -388,6 +429,9 @@ class EmptySingleMultiDelimiter extends QueryPredicateOperator<EmptySingleMultiD
388429

389430
export const queryPredicateOperators = [
390431
new Log(),
432+
new Even(),
433+
new Odd(),
434+
new Text(),
391435
new Type(),
392436
new NotType(),
393437
new TrimEnd(),

packages/cursorless-engine/src/languages/clojure.ts

Lines changed: 0 additions & 162 deletions
This file was deleted.

packages/cursorless-engine/src/languages/getNodeMatcher.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,15 @@
1+
import type { SimpleScopeTypeType } from "@cursorless/common";
12
import { UnsupportedLanguageError } from "@cursorless/common";
23
import type { Node } from "web-tree-sitter";
3-
import type { SimpleScopeTypeType } from "@cursorless/common";
44
import type {
55
NodeMatcher,
66
NodeMatcherValue,
77
SelectionWithEditor,
88
} from "../typings/Types";
99
import { notSupported } from "../util/nodeMatchers";
1010
import { selectionWithEditorFromRange } from "../util/selectionUtils";
11-
import clojure from "./clojure";
12-
import type { LegacyLanguageId } from "./LegacyLanguageId";
1311
import latex from "./latex";
12+
import type { LegacyLanguageId } from "./LegacyLanguageId";
1413
import rust from "./rust";
1514

1615
export function getNodeMatcher(
@@ -41,7 +40,6 @@ export const languageMatchers: Record<
4140
LegacyLanguageId,
4241
Partial<Record<SimpleScopeTypeType, NodeMatcher>>
4342
> = {
44-
clojure,
4543
latex,
4644
rust,
4745
};

0 commit comments

Comments
 (0)