Skip to content

Commit

Permalink
Change implementation of default bounded line for head/tail modifiers (
Browse files Browse the repository at this point in the history
…#2799)

Today's implementation is too quirky with its single versus multiline
behavior. I have multiple times gotten an unexpected behavior.

## Checklist

- [x] I have added
[tests](https://www.cursorless.org/docs/contributing/test-case-recorder/)
- [x] 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]>
  • Loading branch information
AndreasArvidsson and phillco authored Jan 30, 2025
1 parent 2c19566 commit 328a934
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 34 deletions.
26 changes: 26 additions & 0 deletions data/fixtures/recorded/headTail/changeHead3.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
languageId: plaintext
command:
version: 7
spokenForm: change head
action:
name: clearAndSetSelection
target:
type: primitive
modifiers:
- {type: extendThroughStartOf}
usePrePhraseSnapshot: false
initialState:
documentContents: |-
(aaa
)
selections:
- anchor: {line: 0, character: 4}
active: {line: 0, character: 4}
marks: {}
finalState:
documentContents: |-
(
)
selections:
- anchor: {line: 0, character: 1}
active: {line: 0, character: 1}
Original file line number Diff line number Diff line change
Expand Up @@ -253,8 +253,6 @@ export interface SurroundingPairScopeType {
export interface SurroundingPairInteriorScopeType {
type: "surroundingPairInterior";
delimiter: SurroundingPairName;
// If true don't yield multiline pairs
requireSingleLine?: boolean;
}

export interface OneOfScopeType {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import type { HeadModifier, Modifier, TailModifier } from "@cursorless/common";
import {
NoContainingScopeError,
type HeadModifier,
type ScopeType,
type TailModifier,
} from "@cursorless/common";
import type { Target } from "../../typings/target.types";
import type { ModifierStageFactory } from "../ModifierStageFactory";
import type { ModifierStage } from "../PipelineStages.types";
import {
getModifierStagesFromTargetModifiers,
processModifierStages,
} from "../TargetPipelineRunner";
import { HeadTailTarget } from "../targets";
import { HeadTailTarget, PlainTarget } from "../targets";

export class HeadTailStage implements ModifierStage {
constructor(
Expand All @@ -15,34 +20,11 @@ export class HeadTailStage implements ModifierStage {
) {}

run(target: Target): Target[] {
const modifiers: Modifier[] = this.modifier.modifiers ?? [
{
type: "containingScope",
scopeType: {
type: "oneOf",
scopeTypes: [
{
type: "line",
},
{
type: "surroundingPairInterior",
delimiter: "any",
requireSingleLine: true,
},
],
},
},
];

const modifierStages = getModifierStagesFromTargetModifiers(
this.modifierStageFactory,
modifiers,
);
const modifierStages = this.getModifierStages();
const modifiedTargets = processModifierStages(modifierStages, [target]);
const isHead = this.modifier.type === "extendThroughStartOf";

return modifiedTargets.map((modifiedTarget) => {
const isHead = this.modifier.type === "extendThroughStartOf";

return new HeadTailTarget({
editor: target.editor,
isReversed: isHead,
Expand All @@ -52,4 +34,70 @@ export class HeadTailStage implements ModifierStage {
});
});
}

private getModifierStages(): ModifierStage[] {
if (this.modifier.modifiers != null) {
return getModifierStagesFromTargetModifiers(
this.modifierStageFactory,
this.modifier.modifiers,
);
}

return [new BoundedLineStage(this.modifierStageFactory, this.modifier)];
}
}

class BoundedLineStage implements ModifierStage {
constructor(
private modifierStageFactory: ModifierStageFactory,
private modifier: HeadModifier | TailModifier,
) {}

run(target: Target): Target[] {
const line = this.getContainingLine(target);
const pairInterior = this.getContainingPairInterior(target);

const intersection =
pairInterior != null
? line.contentRange.intersection(pairInterior.contentRange)
: null;

if (intersection == null || intersection.isEmpty) {
return [line];
}

return [
new PlainTarget({
editor: target.editor,
isReversed: target.isReversed,
contentRange: intersection,
}),
];
}

private getContainingPairInterior(target: Target): Target | undefined {
try {
return this.getContaining(target, {
type: "surroundingPairInterior",
delimiter: "any",
})[0];
} catch (error) {
if (error instanceof NoContainingScopeError) {
return undefined;
}
throw error;
}
}

private getContainingLine(target: Target): Target {
return this.getContaining(target, {
type: "line",
})[0];
}

private getContaining(target: Target, scopeType: ScopeType): Target[] {
return this.modifierStageFactory
.create({ type: "containingScope", scopeType })
.run(target);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,10 +45,6 @@ export class SurroundingPairInteriorScopeHandler extends BaseScopeHandler {
);

for (const scope of scopes) {
if (this.scopeType.requireSingleLine && !scope.domain.isSingleLine) {
continue;
}

yield {
editor,
domain: scope.domain,
Expand Down
2 changes: 1 addition & 1 deletion packages/cursorless-org-docs/src/docs/user/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ The modifiers `"head"` and `"tail"` can be used to expand a target through the b
- `"take head air"`: selects the mark through to start of the line
- `"take tail air"`: selects the mark through to the end of the line

When inside a single-line surrounding pair (eg parentheses, brackets, etc) the head/tail modifier will only expand to the interior of that pair instead of the whole line. You can explicitly say `"head line"` or `"tail line"` to get the line behavior.
When inside a surrounding pair (eg parentheses, brackets, etc) the head/tail modifier will only expand to the interior of that pair instead of the whole line. You can explicitly say `"head line"` or `"tail line"` to get the line behavior.

When followed by a modifier, they will expand their input to the start or end of the given modifier range. For example:

Expand Down

0 comments on commit 328a934

Please sign in to comment.