diff --git a/cursorless-talon/src/modifiers/modifiers.py b/cursorless-talon/src/modifiers/modifiers.py index 9d275d4d11..7d0e53a1a8 100644 --- a/cursorless-talon/src/modifiers/modifiers.py +++ b/cursorless-talon/src/modifiers/modifiers.py @@ -14,6 +14,8 @@ "just": "toRawSelection", "leading": "leading", "trailing": "trailing", + "content": "keepContentFilter", + "empty": "keepEmptyFilter", } mod.list( diff --git a/src/processTargets/getModifierStage.ts b/src/processTargets/getModifierStage.ts index a2ea266f4c..627bee9203 100644 --- a/src/processTargets/getModifierStage.ts +++ b/src/processTargets/getModifierStage.ts @@ -5,6 +5,10 @@ import { Modifier, } from "../typings/targetDescriptor.types"; import CascadingStage from "./modifiers/CascadingStage"; +import { + KeepContentFilterStage, + KeepEmptyFilterStage, +} from "./modifiers/FilterStages"; import { HeadStage, TailStage } from "./modifiers/HeadTailStage"; import { ExcludeInteriorStage, @@ -65,6 +69,10 @@ export default (modifier: Modifier): ModifierStage => { return new OrdinalScopeStage(modifier); case "relativeScope": return new RelativeScopeStage(modifier); + case "keepContentFilter": + return new KeepContentFilterStage(modifier); + case "keepEmptyFilter": + return new KeepEmptyFilterStage(modifier); case "cascading": return new CascadingStage(modifier); case "modifyIfUntyped": diff --git a/src/processTargets/modifiers/FilterStages.ts b/src/processTargets/modifiers/FilterStages.ts new file mode 100644 index 0000000000..05ed788342 --- /dev/null +++ b/src/processTargets/modifiers/FilterStages.ts @@ -0,0 +1,23 @@ +import type { Target } from "../../typings/target.types"; +import type { + KeepContentFilterModifier, + KeepEmptyFilterModifier, +} from "../../typings/targetDescriptor.types"; +import type { ProcessedTargetsContext } from "../../typings/Types"; +import type { ModifierStage } from "../PipelineStages.types"; + +export class KeepContentFilterStage implements ModifierStage { + constructor(private modifier: KeepContentFilterModifier) {} + + run(context: ProcessedTargetsContext, target: Target): Target[] { + return target.contentText.trim() !== "" ? [target] : []; + } +} + +export class KeepEmptyFilterStage implements ModifierStage { + constructor(private modifier: KeepEmptyFilterModifier) {} + + run(context: ProcessedTargetsContext, target: Target): Target[] { + return target.contentText.trim() === "" ? [target] : []; + } +} diff --git a/src/processTargets/modifiers/scopeTypeStages/LineStage.ts b/src/processTargets/modifiers/scopeTypeStages/LineStage.ts index 990c3576e3..2947c6ded0 100644 --- a/src/processTargets/modifiers/scopeTypeStages/LineStage.ts +++ b/src/processTargets/modifiers/scopeTypeStages/LineStage.ts @@ -27,12 +27,13 @@ export default class implements ModifierStage { const targets: LineTarget[] = []; for (let i = startLine; i <= endLine; ++i) { - const line = editor.document.lineAt(i); - if (!line.isEmptyOrWhitespace) { - targets.push( - createLineTarget(target.editor, target.isReversed, line.range) - ); - } + targets.push( + createLineTarget( + target.editor, + target.isReversed, + editor.document.lineAt(i).range + ) + ); } if (targets.length === 0) { diff --git a/src/test/suite/fixtures/recorded/selectionTypes/clearContent.yml b/src/test/suite/fixtures/recorded/selectionTypes/clearContent.yml new file mode 100644 index 0000000000..fa8f242437 --- /dev/null +++ b/src/test/suite/fixtures/recorded/selectionTypes/clearContent.yml @@ -0,0 +1,37 @@ +languageId: markdown +command: + spokenForm: clear content + version: 3 + targets: + - type: primitive + modifiers: + - {type: keepContentFilter} + usePrePhraseSnapshot: true + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + + a + + b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 1} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 1} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 1} + marks: {} +finalState: + documentContents: |2 + + + + selections: + - anchor: {line: 1, character: 0} + active: {line: 1, character: 0} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 0} +fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: keepContentFilter}]}] diff --git a/src/test/suite/fixtures/recorded/selectionTypes/clearEmpty.yml b/src/test/suite/fixtures/recorded/selectionTypes/clearEmpty.yml new file mode 100644 index 0000000000..f81eb57ad2 --- /dev/null +++ b/src/test/suite/fixtures/recorded/selectionTypes/clearEmpty.yml @@ -0,0 +1,38 @@ +languageId: markdown +command: + spokenForm: clear empty + version: 3 + targets: + - type: primitive + modifiers: + - {type: keepEmptyFilter} + usePrePhraseSnapshot: true + action: {name: clearAndSetSelection} +initialState: + documentContents: |- + + a + + b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 1, character: 0} + active: {line: 1, character: 1} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 1} + - anchor: {line: 3, character: 0} + active: {line: 3, character: 1} + marks: {} +finalState: + documentContents: |- + + a + + b + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} +fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: keepEmptyFilter}]}] diff --git a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine.yml b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine.yml index 585c54df09..28878f942c 100644 --- a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine.yml +++ b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine.yml @@ -30,12 +30,18 @@ finalState: d e f g selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} - anchor: {line: 1, character: 0} active: {line: 1, character: 1} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} - anchor: {line: 3, character: 0} active: {line: 3, character: 5} - anchor: {line: 4, character: 0} active: {line: 4, character: 5} - anchor: {line: 5, character: 0} active: {line: 5, character: 5} + - anchor: {line: 6, character: 0} + active: {line: 6, character: 0} fullTargets: [{type: primitive, mark: {type: cursor}, modifiers: [{type: everyScope, scopeType: line}]}] diff --git a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine2.yml b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine2.yml index 5d10dca7b6..d2c21eca89 100644 --- a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine2.yml +++ b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine2.yml @@ -32,6 +32,8 @@ finalState: selections: - anchor: {line: 1, character: 1} active: {line: 1, character: 0} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} - anchor: {line: 3, character: 5} active: {line: 3, character: 0} - anchor: {line: 4, character: 5} diff --git a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine3.yml b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine3.yml index e68d3bb074..e7bf1df1b5 100644 --- a/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine3.yml +++ b/src/test/suite/fixtures/recorded/selectionTypes/takeEveryLine3.yml @@ -32,6 +32,8 @@ finalState: selections: - anchor: {line: 1, character: 0} active: {line: 1, character: 1} + - anchor: {line: 2, character: 0} + active: {line: 2, character: 0} - anchor: {line: 3, character: 0} active: {line: 3, character: 5} - anchor: {line: 4, character: 0} diff --git a/src/typings/targetDescriptor.types.ts b/src/typings/targetDescriptor.types.ts index c4720f4904..cc687f1f83 100644 --- a/src/typings/targetDescriptor.types.ts +++ b/src/typings/targetDescriptor.types.ts @@ -238,6 +238,14 @@ export interface TrailingModifier { type: "trailing"; } +export interface KeepContentFilterModifier { + type: "keepContentFilter"; +} + +export interface KeepEmptyFilterModifier { + type: "keepEmptyFilter"; +} + export type Position = "before" | "after" | "start" | "end"; export interface PositionModifier { @@ -310,7 +318,9 @@ export type Modifier = | RawSelectionModifier | ModifyIfUntypedModifier | CascadingModifier - | RangeModifier; + | RangeModifier + | KeepContentFilterModifier + | KeepEmptyFilterModifier; export interface PartialRangeTargetDescriptor { type: "range";