Skip to content

Commit 3993034

Browse files
Migrate key and value
1 parent 4b4981f commit 3993034

File tree

5 files changed

+73
-25
lines changed

5 files changed

+73
-25
lines changed

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+
}

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

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

10+
/**
11+
* A predicate operator that returns true if the node matches the desired index parity.
12+
* For example, `(#parity? @foo value 0)` will accept the match if the `@foo`
13+
* capture is at index parity 0 (even) among its parents value children.
14+
*/
15+
class Parity extends QueryPredicateOperator<Parity> {
16+
name = "parity?" as const;
17+
schema = z.tuple([q.node, q.string, q.integer]);
18+
run(
19+
{ document, range, node }: MutableQueryCapture,
20+
fieldName: string,
21+
parity: 0 | 1,
22+
) {
23+
if (node.parent == null) {
24+
return false;
25+
}
26+
27+
const children = getChildNodesForFieldName(node.parent, fieldName);
28+
const nodeIndex = children.findIndex(({ id }) => id === node.id);
29+
30+
if (nodeIndex === -1) {
31+
return false;
32+
}
33+
34+
const desiredIndex = Math.floor(nodeIndex / 2) * 2 + parity;
35+
36+
return nodeIndex === desiredIndex;
37+
}
38+
}
39+
940
/**
1041
* A predicate operator that returns true if the node matches the given text.
1142
* For example, `(#text? @foo bar)` will accept the match if the `@foo`
@@ -402,6 +433,7 @@ class EmptySingleMultiDelimiter extends QueryPredicateOperator<EmptySingleMultiD
402433

403434
export const queryPredicateOperators = [
404435
new Log(),
436+
new Parity(),
405437
new Text(),
406438
new Type(),
407439
new NotType(),

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

Lines changed: 2 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,6 @@ import { patternFinder } from "../util/nodeFinders";
55
import { createPatternMatchers, matcher } from "../util/nodeMatchers";
66
import { getChildNodesForFieldName } from "../util/treeSitterUtils";
77

8-
/**
9-
* Picks a node by rounding down and using the given parity. This function is
10-
* useful for picking the picking eg the key in a sequence of key-value pairs
11-
* @param parentFinder The finder to use to determine whether the parent is a
12-
* match
13-
* @param parity The parity that we're looking for
14-
* @returns A node finder
15-
*/
16-
function parityNodeFinder(parentFinder: NodeFinder, parity: 0 | 1) {
17-
return indexNodeFinder(
18-
parentFinder,
19-
(nodeIndex: number) => Math.floor(nodeIndex / 2) * 2 + parity,
20-
);
21-
}
22-
23-
function mapParityNodeFinder(parity: 0 | 1) {
24-
return parityNodeFinder(patternFinder("map_lit"), parity);
25-
}
26-
278
/**
289
* Creates a node finder which will apply a transformation to the index of a
2910
* value node and return the node at the given index of the nodes parent
@@ -57,6 +38,8 @@ function indexNodeFinder(
5738

5839
const desiredIndex = indexTransform(nodeIndex);
5940

41+
console.log(node.text, nodeIndex, desiredIndex);
42+
6043
if (desiredIndex === -1) {
6144
return null;
6245
}
@@ -78,10 +61,6 @@ const functionCallPattern = "~quoting_lit.list_lit!";
7861
const nodeMatchers: Partial<
7962
Record<SimpleScopeTypeType, NodeMatcherAlternative>
8063
> = {
81-
collectionKey: matcher(mapParityNodeFinder(0)),
82-
value: matcher(mapParityNodeFinder(1)),
83-
84-
// FIXME: Handle formal parameters
8564
argumentOrParameter: matcher(
8665
indexNodeFinder(patternFinder(functionCallPattern), (nodeIndex: number) =>
8766
nodeIndex !== 0 ? nodeIndex : -1,

queries/clojure.scm

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,3 +133,20 @@
133133
(#text? @_dummy "if" "if-let" "when" "when-let")
134134
(#not-parent-type? @ifStatement quoting_lit)
135135
)
136+
137+
;;!! {:foo 1, :bar 2}
138+
;;! ^^^^ ^^^^
139+
;;! ^ ^
140+
(map_lit
141+
value: (_) @collectionKey @collectionKey.domain.start @value.domain.start
142+
value: (_) @value @collectionKey.domain.end @value.domain.end
143+
(#parity? @collectionKey value 0)
144+
(#parity? @value value 1)
145+
)
146+
147+
;;!! {:foo 1, :bar 2}
148+
;;! ^^^^^^^^^^^^^^
149+
(map_lit
150+
"{" @collectionKey.iteration.start.endOf @value.iteration.start.endOf
151+
"}" @collectionKey.iteration.end.startOf @value.iteration.end.startOf
152+
)

0 commit comments

Comments
 (0)