Skip to content

Commit d03f088

Browse files
Change default head/tail scope to be line bounded by matching pairs (#2652)
If no scope is specified for head/tail instead of defaulting to just line we now use the smallest of line or surrounding pair interior. This means that `"take head"` would be bounded by the surrounding pair just like `"short paint"`. If there is no surrounding pair the line is used just like today. You can also override this behavior by specifying line. `"take head line"` would always use the line regardless if you are in a surrounding pair or not. Fixes #2434 The issue mentions a tag for this. I guess since this change is fully extension side it would be a vscode setting, but in tempted to just have this be enabled by default since it's a really useful feature. ## 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: Phil Cohen <[email protected]>
1 parent 6a14ac0 commit d03f088

File tree

8 files changed

+160
-3
lines changed

8 files changed

+160
-3
lines changed

changelog/2024-08-boundedHeadTail.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
tags: [enhancement]
3+
pullRequest: 2652
4+
---
5+
6+
If you're inside of a surrounding pair, the "head" and "tail" modifiers now only expand to the interior of that pair, instead of the whole line. This is more useful, akin to how "short paint" works. You can explicitly say "head line" or "tail line" to get the old behavior.
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: change head
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- {type: extendThroughStartOf}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: foo bar
14+
selections:
15+
- anchor: {line: 0, character: 4}
16+
active: {line: 0, character: 4}
17+
marks: {}
18+
finalState:
19+
documentContents: bar
20+
selections:
21+
- anchor: {line: 0, character: 0}
22+
active: {line: 0, character: 0}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: change head
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- {type: extendThroughStartOf}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: "'foo bar'"
14+
selections:
15+
- anchor: {line: 0, character: 5}
16+
active: {line: 0, character: 5}
17+
marks: {}
18+
finalState:
19+
documentContents: "'bar'"
20+
selections:
21+
- anchor: {line: 0, character: 1}
22+
active: {line: 0, character: 1}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: change tail
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- {type: extendThroughEndOf}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: foo bar
14+
selections:
15+
- anchor: {line: 0, character: 3}
16+
active: {line: 0, character: 3}
17+
marks: {}
18+
finalState:
19+
documentContents: foo
20+
selections:
21+
- anchor: {line: 0, character: 3}
22+
active: {line: 0, character: 3}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: change tail
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- {type: extendThroughEndOf}
11+
usePrePhraseSnapshot: true
12+
initialState:
13+
documentContents: "'foo bar'"
14+
selections:
15+
- anchor: {line: 0, character: 4}
16+
active: {line: 0, character: 4}
17+
marks: {}
18+
finalState:
19+
documentContents: "'foo'"
20+
selections:
21+
- anchor: {line: 0, character: 4}
22+
active: {line: 0, character: 4}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: clear head line
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: extendThroughStartOf
11+
modifiers:
12+
- type: preferredScope
13+
scopeType: {type: line}
14+
usePrePhraseSnapshot: true
15+
spokenFormError: Modifier 'preferredScope'
16+
initialState:
17+
documentContents: "'foo bar'"
18+
selections:
19+
- anchor: {line: 0, character: 5}
20+
active: {line: 0, character: 5}
21+
marks: {}
22+
finalState:
23+
documentContents: bar'
24+
selections:
25+
- anchor: {line: 0, character: 0}
26+
active: {line: 0, character: 0}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
languageId: plaintext
2+
command:
3+
version: 7
4+
spokenForm: clear tail line
5+
action:
6+
name: clearAndSetSelection
7+
target:
8+
type: primitive
9+
modifiers:
10+
- type: extendThroughEndOf
11+
modifiers:
12+
- type: preferredScope
13+
scopeType: {type: line}
14+
usePrePhraseSnapshot: true
15+
spokenFormError: Modifier 'preferredScope'
16+
initialState:
17+
documentContents: "'foo bar'"
18+
selections:
19+
- anchor: {line: 0, character: 4}
20+
active: {line: 0, character: 4}
21+
marks: {}
22+
finalState:
23+
documentContents: "'foo"
24+
selections:
25+
- anchor: {line: 0, character: 4}
26+
active: {line: 0, character: 4}

packages/cursorless-engine/src/processTargets/modifiers/HeadTailStage.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { HeadModifier, TailModifier } from "@cursorless/common";
1+
import type { HeadModifier, Modifier, TailModifier } from "@cursorless/common";
22
import type { Target } from "../../typings/target.types";
33
import type { ModifierStageFactory } from "../ModifierStageFactory";
44
import type { ModifierStage } from "../PipelineStages.types";
@@ -15,10 +15,21 @@ export class HeadTailStage implements ModifierStage {
1515
) {}
1616

1717
run(target: Target): Target[] {
18-
const modifiers = this.modifier.modifiers ?? [
18+
const modifiers: Modifier[] = this.modifier.modifiers ?? [
1919
{
2020
type: "containingScope",
21-
scopeType: { type: "line" },
21+
scopeType: {
22+
type: "oneOf",
23+
scopeTypes: [
24+
{
25+
type: "line",
26+
},
27+
{
28+
type: "surroundingPairInterior",
29+
delimiter: "any",
30+
},
31+
],
32+
},
2233
},
2334
];
2435

0 commit comments

Comments
 (0)