Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change implementation of default bounded line for head/tail modifiers #2799

Merged
merged 5 commits into from
Jan 30, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading