Skip to content

Commit 0b9cdc3

Browse files
AndreasArvidssonpokeyphillco
authored
Proper handling of surrounding pair delimiter prefixes (#2705)
On main `"take inside"` on `"command server"` will only select `command serve` because `r"` is considered a delimiter. This pull request fixes this. ## Checklist - [x] I have added [tests](https://www.cursorless.org/docs/contributing/test-case-recorder/) - [/] I have updated the [docs](https://github.com/cursorless-dev/cursorless/tree/main/docs) and [cheatsheet](https://github.com/cursorless-dev/cursorless/tree/main/cursorless-talon/src/cheatsheet) - [/] I have not broken the cheatsheet --------- Co-authored-by: Pokey Rule <[email protected]> Co-authored-by: Phil Cohen <[email protected]>
1 parent 9d90d16 commit 0b9cdc3

File tree

16 files changed

+175
-80
lines changed

16 files changed

+175
-80
lines changed

cursorless-talon/src/spoken_forms.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,7 @@ def handle_new_values(csv_name: str, values: list[SpokenFormEntry]):
160160
"private.switchStatementSubject",
161161
"textFragment",
162162
"disqualifyDelimiter",
163+
"pairDelimiter",
163164
],
164165
default_list_name="scope_type",
165166
),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: python
2+
command:
3+
version: 7
4+
spokenForm: change inside
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- {type: interiorOnly}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: r'command server'
14+
selections:
15+
- anchor: {line: 0, character: 2}
16+
active: {line: 0, character: 2}
17+
marks: {}
18+
finalState:
19+
documentContents: r''
20+
selections:
21+
- anchor: {line: 0, character: 2}
22+
active: {line: 0, character: 2}

data/fixtures/recorded/surroundingPair/parseTree/python/changePair2.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ command:
1111
scopeType: {type: surroundingPair, delimiter: any}
1212
usePrePhraseSnapshot: true
1313
initialState:
14-
documentContents: "\" r\""
14+
documentContents: "' r'"
1515
selections:
1616
- anchor: {line: 0, character: 0}
1717
active: {line: 0, character: 0}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
languageId: python
2+
command:
3+
version: 7
4+
spokenForm: change pair
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: containingScope
11+
scopeType: {type: surroundingPair, delimiter: any}
12+
usePrePhraseSnapshot: true
13+
initialState:
14+
documentContents: r'command server'
15+
selections:
16+
- anchor: {line: 0, character: 2}
17+
active: {line: 0, character: 2}
18+
marks: {}
19+
finalState:
20+
documentContents: ""
21+
selections:
22+
- anchor: {line: 0, character: 0}
23+
active: {line: 0, character: 0}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"server"
2+
'server'
3+
"""server"""
4+
'''server'''
5+
r" r"
6+
r' r'
7+
r""" r"""
8+
r''' r'''
9+
---
10+
11+
[#1 Content] =
12+
[#1 Domain] = 4:0-4:2
13+
>--<
14+
4| r" r"
15+
16+
[#1 Removal] = 4:0-4:3
17+
>---<
18+
4| r" r"
19+
20+
[#1 Trailing delimiter] = 4:2-4:3
21+
>-<
22+
4| r" r"
23+
24+
[#1 Insertion delimiter] = " "
25+
26+
27+
[#2 Content] =
28+
[#2 Domain] = 5:0-5:2
29+
>--<
30+
5| r' r'
31+
32+
[#2 Removal] = 5:0-5:3
33+
>---<
34+
5| r' r'
35+
36+
[#2 Trailing delimiter] = 5:2-5:3
37+
>-<
38+
5| r' r'
39+
40+
[#2 Insertion delimiter] = " "
41+
42+
43+
[#3 Content] =
44+
[#3 Domain] = 6:0-6:4
45+
>----<
46+
6| r""" r"""
47+
48+
[#3 Removal] = 6:0-6:5
49+
>-----<
50+
6| r""" r"""
51+
52+
[#3 Trailing delimiter] = 6:4-6:5
53+
>-<
54+
6| r""" r"""
55+
56+
[#3 Insertion delimiter] = " "
57+
58+
59+
[#4 Content] =
60+
[#4 Domain] = 7:0-7:4
61+
>----<
62+
7| r''' r'''
63+
64+
[#4 Removal] = 7:0-7:5
65+
>-----<
66+
7| r''' r'''
67+
68+
[#4 Trailing delimiter] = 7:4-7:5
69+
>-<
70+
7| r''' r'''
71+
72+
[#4 Insertion delimiter] = " "

packages/common/src/scopeSupportFacets/python.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ export const pythonScopeSupport: LanguageScopeSupportFacetMap = {
1414
namedFunction: supported,
1515
anonymousFunction: supported,
1616
disqualifyDelimiter: supported,
17+
pairDelimiter: supported,
1718

1819
"argument.actual": supported,
1920
"argument.actual.iteration": supported,

packages/common/src/scopeSupportFacets/scopeSupportFacetInfos.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,11 @@ export const scopeSupportFacetInfos: Record<
298298
"Used to disqualify a token from being treated as a surrounding pair delimiter. This will usually be operators containing `>` or `<`, eg `<`, `<=`, `->`, etc",
299299
scopeType: "disqualifyDelimiter",
300300
},
301+
pairDelimiter: {
302+
description:
303+
"A pair delimiter, eg parentheses, brackets, braces, quotes, etc",
304+
scopeType: "pairDelimiter",
305+
},
301306

302307
"branch.if": {
303308
description: "An if/elif/else branch",

packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,7 @@ export const scopeSupportFacets = [
8080
"textFragment.string.multiLine",
8181

8282
"disqualifyDelimiter",
83+
"pairDelimiter",
8384

8485
"branch.if",
8586
"branch.if.iteration",

packages/common/src/types/command/PartialTargetDescriptor.types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ export const simpleScopeTypeTypes = [
205205
// Private scope types
206206
"textFragment",
207207
"disqualifyDelimiter",
208+
"pairDelimiter",
208209
] as const;
209210

210211
export function isSimpleScopeType(

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,21 @@ class HasMultipleChildrenOfType extends QueryPredicateOperator<HasMultipleChildr
6262
}
6363
}
6464

65+
/**
66+
* A predicate operator that returns true if the nodes text matched the regular expression
67+
*/
68+
class Match extends QueryPredicateOperator<Match> {
69+
name = "match?" as const;
70+
schema = z.tuple([q.node, q.string]);
71+
72+
run(nodeInfo: MutableQueryCapture, pattern: string) {
73+
const { document, range } = nodeInfo;
74+
const regex = new RegExp(pattern, "ds");
75+
const text = document.getText(range);
76+
return regex.test(text);
77+
}
78+
}
79+
6580
class ChildRange extends QueryPredicateOperator<ChildRange> {
6681
name = "child-range!" as const;
6782
schema = z.union([
@@ -277,4 +292,5 @@ export const queryPredicateOperators = [
277292
new InsertionDelimiter(),
278293
new SingleOrMultilineDelimiter(),
279294
new HasMultipleChildrenOfType(),
295+
new Match(),
280296
];

0 commit comments

Comments
 (0)