From e27de55f3a831b4718f9bf8bc8110a91342b0139 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 31 Oct 2025 11:29:14 +0100 Subject: [PATCH 1/5] Added expandToFullLine modifier --- cursorless-talon/src/spoken_forms.json | 1 + .../recorded/modifiers/changeExpand.yml | 22 +++++++++++++++++++ .../command/PartialTargetDescriptor.types.ts | 5 +++++ .../ModifierStageFactoryImpl.ts | 3 +++ .../modifiers/ExpandToFullLineStage.ts | 20 +++++++++++++++++ .../spokenForms/defaultSpokenFormMapCore.ts | 1 + .../src/util/getScopeType.ts | 1 + 7 files changed, 53 insertions(+) create mode 100644 data/fixtures/recorded/modifiers/changeExpand.yml create mode 100644 packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 243d75cbb4..3d1f323642 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -89,6 +89,7 @@ "trailing": "trailing", "content": "keepContentFilter", "empty": "keepEmptyFilter", + "expand": "expandToFullLine", "its": "inferPreviousMark", "visible": "visible" }, diff --git a/data/fixtures/recorded/modifiers/changeExpand.yml b/data/fixtures/recorded/modifiers/changeExpand.yml new file mode 100644 index 0000000000..ccaaa4f1e3 --- /dev/null +++ b/data/fixtures/recorded/modifiers/changeExpand.yml @@ -0,0 +1,22 @@ +languageId: plaintext +command: + version: 7 + spokenForm: change expand + action: + name: clearAndSetSelection + target: + type: primitive + modifiers: + - {type: expandToFullLine} + usePrePhraseSnapshot: false +initialState: + documentContents: " foo " + selections: + - anchor: {line: 0, character: 4} + active: {line: 0, character: 4} + marks: {} +finalState: + documentContents: "" + selections: + - anchor: {line: 0, character: 0} + active: {line: 0, character: 0} diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index cbb68b08a9..2871e99c7d 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -397,6 +397,10 @@ export interface TrailingModifier { type: "trailing"; } +export interface ExpandToFullLineModifier { + type: "expandToFullLine"; +} + export interface KeepContentFilterModifier { type: "keepContentFilter"; } @@ -482,6 +486,7 @@ export type Modifier = | LeadingModifier | TrailingModifier | RawSelectionModifier + | ExpandToFullLineModifier | ModifyIfUntypedModifier | FallbackModifier | RangeModifier diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index 5ad4c56a5a..27d361c3cb 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -7,6 +7,7 @@ import { ClassFunctionNameStage } from "./modifiers/ClassFunctionNameStage"; import { ModifyIfUntypedStage } from "./modifiers/ConditionalModifierStages"; import { ContainingScopeStage } from "./modifiers/ContainingScopeStage"; import { EveryScopeStage } from "./modifiers/EveryScopeStage"; +import { ExpandToFullLineStage } from "./modifiers/ExpandToFullLineStage"; import { FallbackStage } from "./modifiers/FallbackStage"; import { KeepContentFilterStage, @@ -57,6 +58,8 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { return new LeadingStage(this, modifier); case "trailing": return new TrailingStage(this, modifier); + case "expandToFullLine": + return new ExpandToFullLineStage(modifier); case "visible": return new VisibleStage(modifier); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts new file mode 100644 index 0000000000..5938174e4d --- /dev/null +++ b/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts @@ -0,0 +1,20 @@ +import type { ExpandToFullLineModifier } from "@cursorless/common"; +import type { Target } from "../../typings/target.types"; +import { expandToFullLine } from "../../util/rangeUtils"; +import type { ModifierStage } from "../PipelineStages.types"; +import { LineTarget, RawSelectionTarget } from "../targets"; + +export class ExpandToFullLineStage implements ModifierStage { + constructor(private modifier: ExpandToFullLineModifier) {} + + run(target: Target): Target[] { + const contentRange = expandToFullLine(target.editor, target.contentRange); + return [ + new LineTarget({ + editor: target.editor, + contentRange, + isReversed: target.isReversed, + }), + ]; + } +} diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 064117a802..7aa7e9c309 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -122,6 +122,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { toRawSelection: "just", leading: "leading", trailing: "trailing", + expandToFullLine: "expand", keepContentFilter: "content", keepEmptyFilter: "empty", inferPreviousMark: "its", diff --git a/packages/cursorless-engine/src/util/getScopeType.ts b/packages/cursorless-engine/src/util/getScopeType.ts index 1d6ad3b7cb..57378b64a8 100644 --- a/packages/cursorless-engine/src/util/getScopeType.ts +++ b/packages/cursorless-engine/src/util/getScopeType.ts @@ -22,6 +22,7 @@ export function getScopeType(modifier: Modifier): ScopeType | undefined { case "endOf": case "extendThroughStartOf": case "extendThroughEndOf": + case "expandToFullLine": case "fallback": case "range": case "modifyIfUntyped": From 415ecb41dd98d2e148e0a9172216ab690d3d1d01 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Fri, 31 Oct 2025 11:35:27 +0100 Subject: [PATCH 2/5] Remove unused import --- .../src/processTargets/modifiers/ExpandToFullLineStage.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts index 5938174e4d..bc960b25a7 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts @@ -2,7 +2,7 @@ import type { ExpandToFullLineModifier } from "@cursorless/common"; import type { Target } from "../../typings/target.types"; import { expandToFullLine } from "../../util/rangeUtils"; import type { ModifierStage } from "../PipelineStages.types"; -import { LineTarget, RawSelectionTarget } from "../targets"; +import { LineTarget } from "../targets"; export class ExpandToFullLineStage implements ModifierStage { constructor(private modifier: ExpandToFullLineModifier) {} From 9b6324e8182c8dc004e704503a9e89452d74f691 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 4 Nov 2025 09:42:12 +0100 Subject: [PATCH 3/5] Use scope instead of modifier --- cursorless-talon/src/spoken_forms.json | 2 +- .../recorded/modifiers/changeExpand.yml | 22 ----- data/fixtures/scopes/plaintext/fullLine.scope | 10 ++ .../fixtures/scopes/plaintext/fullLine2.scope | 96 +++++++++++++++++++ data/scopeSupportFacetInfos.md | 6 +- .../PlaintextScopeSupportFacetInfos.ts | 8 +- .../scopeSupportFacets.types.ts | 1 + .../command/PartialTargetDescriptor.types.ts | 6 +- .../ModifierStageFactoryImpl.ts | 3 - .../modifiers/ExpandToFullLineStage.ts | 20 ---- .../scopeHandlers/LineScopeHandler.ts | 26 +++-- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 4 +- .../src/scopeProviders/ScopeInfoProvider.ts | 1 + .../spokenForms/defaultSpokenFormMapCore.ts | 2 +- .../src/util/getScopeType.ts | 1 - .../src/docs/contributing/scopes/fullLine.mdx | 5 + 16 files changed, 151 insertions(+), 62 deletions(-) delete mode 100644 data/fixtures/recorded/modifiers/changeExpand.yml create mode 100644 data/fixtures/scopes/plaintext/fullLine.scope create mode 100644 data/fixtures/scopes/plaintext/fullLine2.scope delete mode 100644 packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts create mode 100644 packages/cursorless-org-docs/src/docs/contributing/scopes/fullLine.mdx diff --git a/cursorless-talon/src/spoken_forms.json b/cursorless-talon/src/spoken_forms.json index 3d1f323642..dd332db2ab 100644 --- a/cursorless-talon/src/spoken_forms.json +++ b/cursorless-talon/src/spoken_forms.json @@ -89,7 +89,6 @@ "trailing": "trailing", "content": "keepContentFilter", "empty": "keepEmptyFilter", - "expand": "expandToFullLine", "its": "inferPreviousMark", "visible": "visible" }, @@ -175,6 +174,7 @@ "token": "token", "identifier": "identifier", "line": "line", + "full line": "fullLine", "sentence": "sentence", "block": "paragraph", "file": "document", diff --git a/data/fixtures/recorded/modifiers/changeExpand.yml b/data/fixtures/recorded/modifiers/changeExpand.yml deleted file mode 100644 index ccaaa4f1e3..0000000000 --- a/data/fixtures/recorded/modifiers/changeExpand.yml +++ /dev/null @@ -1,22 +0,0 @@ -languageId: plaintext -command: - version: 7 - spokenForm: change expand - action: - name: clearAndSetSelection - target: - type: primitive - modifiers: - - {type: expandToFullLine} - usePrePhraseSnapshot: false -initialState: - documentContents: " foo " - selections: - - anchor: {line: 0, character: 4} - active: {line: 0, character: 4} - marks: {} -finalState: - documentContents: "" - selections: - - anchor: {line: 0, character: 0} - active: {line: 0, character: 0} diff --git a/data/fixtures/scopes/plaintext/fullLine.scope b/data/fixtures/scopes/plaintext/fullLine.scope new file mode 100644 index 0000000000..380ee058e5 --- /dev/null +++ b/data/fixtures/scopes/plaintext/fullLine.scope @@ -0,0 +1,10 @@ + Hello world +--- + +[Content] = +[Removal] = +[Domain] = 0:0-0:17 + >-----------------< +0| Hello world + +[Insertion delimiter] = "\n" diff --git a/data/fixtures/scopes/plaintext/fullLine2.scope b/data/fixtures/scopes/plaintext/fullLine2.scope new file mode 100644 index 0000000000..a7bd13fa2f --- /dev/null +++ b/data/fixtures/scopes/plaintext/fullLine2.scope @@ -0,0 +1,96 @@ + +Hello + world + +--- + +[#1 Content] = +[#1 Domain] = 0:0-0:0 + >< +0| + +[#1 Removal] = 0:0-1:0 + > +0| +1| Hello + < + +[#1 Trailing delimiter] = 0:0-1:0 + > +0| +1| Hello + < + +[#1 Insertion delimiter] = "\n" + + +[#2 Content] = +[#2 Domain] = 1:0-1:5 + >-----< +1| Hello + +[#2 Removal] = 1:0-2:0 + >----- +1| Hello +2| world + < + +[#2 Leading delimiter] = 0:0-1:0 + > +0| +1| Hello + < + +[#2 Trailing delimiter] = 1:5-2:0 + > +1| Hello +2| world + < + +[#2 Insertion delimiter] = "\n" + + +[#3 Content] = +[#3 Domain] = 2:0-2:7 + >-------< +2| world + +[#3 Removal] = 2:0-3:0 + >------- +2| world +3| + < + +[#3 Leading delimiter] = 1:5-2:0 + > +1| Hello +2| world + < + +[#3 Trailing delimiter] = 2:7-3:0 + > +2| world +3| + < + +[#3 Insertion delimiter] = "\n" + + +[#4 Content] = +[#4 Domain] = 3:0-3:2 + >--< +3| + +[#4 Removal] = 2:7-3:2 + > +2| world +3| + --< + +[#4 Leading delimiter] = 2:7-3:0 + > +2| world +3| + < + +[#4 Insertion delimiter] = "\n" diff --git a/data/scopeSupportFacetInfos.md b/data/scopeSupportFacetInfos.md index aa92ff1fb7..c79ccfa6b2 100644 --- a/data/scopeSupportFacetInfos.md +++ b/data/scopeSupportFacetInfos.md @@ -141,6 +141,10 @@ - `fieldAccess` A field access +### fullLine + +- `fullLine` A single full line in the document. Includes leading and trailing whitespace. + ### functionCall - `functionCall` A function call @@ -196,7 +200,7 @@ ### line -- `line` A single line in the document +- `line` A single line in the document. Excludes leading and trailing whitespace. ### list diff --git a/packages/common/src/scopeSupportFacets/PlaintextScopeSupportFacetInfos.ts b/packages/common/src/scopeSupportFacets/PlaintextScopeSupportFacetInfos.ts index 5192a950a8..03d410fff9 100644 --- a/packages/common/src/scopeSupportFacets/PlaintextScopeSupportFacetInfos.ts +++ b/packages/common/src/scopeSupportFacets/PlaintextScopeSupportFacetInfos.ts @@ -24,9 +24,15 @@ export const plaintextScopeSupportFacetInfos: Record< scopeType: "identifier", }, line: { - description: "A single line in the document", + description: + "A single line in the document. Excludes leading and trailing whitespace.", scopeType: "line", }, + fullLine: { + description: + "A single full line in the document. Includes leading and trailing whitespace.", + scopeType: "fullLine", + }, sentence: { description: "A single sentence in the document", scopeType: "sentence", diff --git a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts index 455740f6f9..0b21f3c068 100644 --- a/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts +++ b/packages/common/src/scopeSupportFacets/scopeSupportFacets.types.ts @@ -303,6 +303,7 @@ export type PlaintextScopeSupportFacet = | "token" | "identifier" | "line" + | "fullLine" | "sentence" | "paragraph" | "boundedParagraph" diff --git a/packages/common/src/types/command/PartialTargetDescriptor.types.ts b/packages/common/src/types/command/PartialTargetDescriptor.types.ts index 2871e99c7d..a56e14bbc4 100644 --- a/packages/common/src/types/command/PartialTargetDescriptor.types.ts +++ b/packages/common/src/types/command/PartialTargetDescriptor.types.ts @@ -193,6 +193,7 @@ export const simpleScopeTypeTypes = [ "token", "identifier", "line", + "fullLine", "sentence", "paragraph", "boundedParagraph", @@ -397,10 +398,6 @@ export interface TrailingModifier { type: "trailing"; } -export interface ExpandToFullLineModifier { - type: "expandToFullLine"; -} - export interface KeepContentFilterModifier { type: "keepContentFilter"; } @@ -486,7 +483,6 @@ export type Modifier = | LeadingModifier | TrailingModifier | RawSelectionModifier - | ExpandToFullLineModifier | ModifyIfUntypedModifier | FallbackModifier | RangeModifier diff --git a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts index 27d361c3cb..5ad4c56a5a 100644 --- a/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/ModifierStageFactoryImpl.ts @@ -7,7 +7,6 @@ import { ClassFunctionNameStage } from "./modifiers/ClassFunctionNameStage"; import { ModifyIfUntypedStage } from "./modifiers/ConditionalModifierStages"; import { ContainingScopeStage } from "./modifiers/ContainingScopeStage"; import { EveryScopeStage } from "./modifiers/EveryScopeStage"; -import { ExpandToFullLineStage } from "./modifiers/ExpandToFullLineStage"; import { FallbackStage } from "./modifiers/FallbackStage"; import { KeepContentFilterStage, @@ -58,8 +57,6 @@ export class ModifierStageFactoryImpl implements ModifierStageFactory { return new LeadingStage(this, modifier); case "trailing": return new TrailingStage(this, modifier); - case "expandToFullLine": - return new ExpandToFullLineStage(modifier); case "visible": return new VisibleStage(modifier); diff --git a/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts b/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts deleted file mode 100644 index bc960b25a7..0000000000 --- a/packages/cursorless-engine/src/processTargets/modifiers/ExpandToFullLineStage.ts +++ /dev/null @@ -1,20 +0,0 @@ -import type { ExpandToFullLineModifier } from "@cursorless/common"; -import type { Target } from "../../typings/target.types"; -import { expandToFullLine } from "../../util/rangeUtils"; -import type { ModifierStage } from "../PipelineStages.types"; -import { LineTarget } from "../targets"; - -export class ExpandToFullLineStage implements ModifierStage { - constructor(private modifier: ExpandToFullLineModifier) {} - - run(target: Target): Target[] { - const contentRange = expandToFullLine(target.editor, target.contentRange); - return [ - new LineTarget({ - editor: target.editor, - contentRange, - isReversed: target.isReversed, - }), - ]; - } -} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts index da529958c5..d77961902e 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts @@ -9,15 +9,21 @@ import { LineTarget } from "../../targets"; import { BaseScopeHandler } from "./BaseScopeHandler"; import type { TargetScope } from "./scope.types"; +interface LineScopeType { + type: "line" | "fullLine"; +} + export class LineScopeHandler extends BaseScopeHandler { - public readonly scopeType = { type: "line" } as const; public readonly iterationScopeType: ScopeType = { type: "paragraph", } as const; protected readonly isHierarchical = false; public readonly includeAdjacentInEvery = true; - constructor(_scopeType: ScopeType, _languageId: string) { + constructor( + public readonly scopeType: LineScopeType, + _languageId: string, + ) { super(); } @@ -26,13 +32,15 @@ export class LineScopeHandler extends BaseScopeHandler { position: Position, direction: Direction, ): Iterable { + const useFullLine = this.scopeType.type === "fullLine"; + if (direction === "forward") { for (let i = position.line; i < editor.document.lineCount; i++) { - yield lineNumberToScope(editor, i); + yield lineNumberToScope(editor, useFullLine, i); } } else { for (let i = position.line; i >= 0; i--) { - yield lineNumberToScope(editor, i); + yield lineNumberToScope(editor, useFullLine, i); } } } @@ -40,6 +48,7 @@ export class LineScopeHandler extends BaseScopeHandler { function lineNumberToScope( editor: TextEditor, + useFullLine: boolean, lineNumber: number, ): TargetScope { const { range } = editor.document.lineAt(lineNumber); @@ -47,7 +56,9 @@ function lineNumberToScope( return { editor, domain: range, - getTargets: (isReversed) => [createLineTarget(editor, isReversed, range)], + getTargets: (isReversed) => [ + createLineTarget(editor, isReversed, range, useFullLine), + ], }; } @@ -55,11 +66,14 @@ export function createLineTarget( editor: TextEditor, isReversed: boolean, range: Range, + useUnmodifiedRange = false, ) { return new LineTarget({ editor, isReversed, - contentRange: fitRangeToLineContent(editor, range), + contentRange: useUnmodifiedRange + ? range + : fitRangeToLineContent(editor, range), }); } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index b23314d897..bc981d3ae4 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -65,7 +65,9 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "identifier": return new IdentifierScopeHandler(this, scopeType, languageId); case "line": - return new LineScopeHandler(scopeType, languageId); + return new LineScopeHandler({ type: scopeType.type }, languageId); + case "fullLine": + return new LineScopeHandler({ type: scopeType.type }, languageId); case "sentence": return new SentenceScopeHandler(this, scopeType, languageId); case "paragraph": diff --git a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts index 2569c4812e..48dc157edb 100644 --- a/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts +++ b/packages/cursorless-engine/src/scopeProviders/ScopeInfoProvider.ts @@ -171,6 +171,7 @@ function isLanguageSpecific(scopeType: ScopeType): boolean { case "token": case "identifier": case "line": + case "fullLine": case "sentence": case "paragraph": case "boundedParagraph": diff --git a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts index 7aa7e9c309..518470e8a8 100644 --- a/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts +++ b/packages/cursorless-engine/src/spokenForms/defaultSpokenFormMapCore.ts @@ -93,6 +93,7 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { token: "token", identifier: "identifier", line: "line", + fullLine: "full line", sentence: "sentence", paragraph: "block", boundedParagraph: "short block", @@ -122,7 +123,6 @@ export const defaultSpokenFormMapCore: DefaultSpokenFormMapDefinition = { toRawSelection: "just", leading: "leading", trailing: "trailing", - expandToFullLine: "expand", keepContentFilter: "content", keepEmptyFilter: "empty", inferPreviousMark: "its", diff --git a/packages/cursorless-engine/src/util/getScopeType.ts b/packages/cursorless-engine/src/util/getScopeType.ts index 57378b64a8..1d6ad3b7cb 100644 --- a/packages/cursorless-engine/src/util/getScopeType.ts +++ b/packages/cursorless-engine/src/util/getScopeType.ts @@ -22,7 +22,6 @@ export function getScopeType(modifier: Modifier): ScopeType | undefined { case "endOf": case "extendThroughStartOf": case "extendThroughEndOf": - case "expandToFullLine": case "fallback": case "range": case "modifyIfUntyped": diff --git a/packages/cursorless-org-docs/src/docs/contributing/scopes/fullLine.mdx b/packages/cursorless-org-docs/src/docs/contributing/scopes/fullLine.mdx new file mode 100644 index 0000000000..8d35c2dbf7 --- /dev/null +++ b/packages/cursorless-org-docs/src/docs/contributing/scopes/fullLine.mdx @@ -0,0 +1,5 @@ +import { Scopes } from "./components/Scopes"; + +# Full line + + From efaaab526c269725024a42506ebf9b08509f8c58 Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 4 Nov 2025 09:58:18 +0100 Subject: [PATCH 4/5] Clean up --- .../processTargets/marks/LineNumberStage.ts | 4 +-- .../scopeHandlers/LineScopeHandler.ts | 28 +++++++------------ .../scopeHandlers/ParagraphScopeHandler.ts | 12 ++++++-- .../scopeHandlers/ScopeHandlerFactoryImpl.ts | 1 - 4 files changed, 22 insertions(+), 23 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts b/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts index 97c4a2f203..329ebdeec6 100644 --- a/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts +++ b/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts @@ -21,8 +21,8 @@ export class LineNumberStage implements MarkStage { this.mark.lineNumberType, this.mark.lineNumber, ); - const contentRange = editor.document.lineAt(lineNumber).range; - return [createLineTarget(editor, false, contentRange)]; + const line = editor.document.lineAt(lineNumber); + return [createLineTarget(editor, false, line)]; } } diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts index d77961902e..9cd24e64b2 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts @@ -3,8 +3,8 @@ import type { Position, ScopeType, TextEditor, + TextLine, } from "@cursorless/common"; -import { Range } from "@cursorless/common"; import { LineTarget } from "../../targets"; import { BaseScopeHandler } from "./BaseScopeHandler"; import type { TargetScope } from "./scope.types"; @@ -51,13 +51,13 @@ function lineNumberToScope( useFullLine: boolean, lineNumber: number, ): TargetScope { - const { range } = editor.document.lineAt(lineNumber); + const line = editor.document.lineAt(lineNumber); return { editor, - domain: range, + domain: line.range, getTargets: (isReversed) => [ - createLineTarget(editor, isReversed, range, useFullLine), + createLineTarget(editor, isReversed, line, useFullLine), ], }; } @@ -65,23 +65,15 @@ function lineNumberToScope( export function createLineTarget( editor: TextEditor, isReversed: boolean, - range: Range, - useUnmodifiedRange = false, + line: TextLine, + useFullRange = false, ) { return new LineTarget({ editor, isReversed, - contentRange: useUnmodifiedRange - ? range - : fitRangeToLineContent(editor, range), + contentRange: + useFullRange || line.rangeTrimmed == null + ? line.range + : line.rangeTrimmed, }); } - -export function fitRangeToLineContent(editor: TextEditor, range: Range) { - const startLine = editor.document.lineAt(range.start); - const endLine = editor.document.lineAt(range.end); - return new Range( - startLine.rangeTrimmed?.start ?? startLine.range.start, - endLine.rangeTrimmed?.end ?? endLine.range.end, - ); -} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ParagraphScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ParagraphScopeHandler.ts index 54c36fd315..6da0f0a55f 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ParagraphScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ParagraphScopeHandler.ts @@ -1,15 +1,14 @@ import type { Direction, Position, - Range, ScopeType, TextDocument, TextEditor, TextLine, } from "@cursorless/common"; +import { Range } from "@cursorless/common"; import { ParagraphTarget } from "../../targets"; import { BaseScopeHandler } from "./BaseScopeHandler"; -import { fitRangeToLineContent } from "./LineScopeHandler"; import type { TargetScope } from "./scope.types"; export class ParagraphScopeHandler extends BaseScopeHandler { @@ -95,3 +94,12 @@ function createScope(editor: TextEditor, domain: Range): TargetScope { ], }; } + +function fitRangeToLineContent(editor: TextEditor, range: Range) { + const startLine = editor.document.lineAt(range.start); + const endLine = editor.document.lineAt(range.end); + return new Range( + startLine.rangeTrimmed?.start ?? startLine.range.start, + endLine.rangeTrimmed?.end ?? endLine.range.end, + ); +} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts index bc981d3ae4..e3b85c0a6a 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/ScopeHandlerFactoryImpl.ts @@ -65,7 +65,6 @@ export class ScopeHandlerFactoryImpl implements ScopeHandlerFactory { case "identifier": return new IdentifierScopeHandler(this, scopeType, languageId); case "line": - return new LineScopeHandler({ type: scopeType.type }, languageId); case "fullLine": return new LineScopeHandler({ type: scopeType.type }, languageId); case "sentence": From c5e28981f091b8e18fe0dc9487d6cdc5d890741b Mon Sep 17 00:00:00 2001 From: Andreas Arvidsson Date: Tue, 4 Nov 2025 10:02:05 +0100 Subject: [PATCH 5/5] More clean up --- .../processTargets/marks/LineNumberStage.ts | 2 +- .../scopeHandlers/LineScopeHandler.ts | 19 +------------------ .../src/processTargets/targets/LineTarget.ts | 18 +++++++++++++++++- 3 files changed, 19 insertions(+), 20 deletions(-) diff --git a/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts b/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts index 329ebdeec6..9803e67430 100644 --- a/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts +++ b/packages/cursorless-engine/src/processTargets/marks/LineNumberStage.ts @@ -5,8 +5,8 @@ import type { } from "@cursorless/common"; import { ide } from "../../singletons/ide.singleton"; import type { MarkStage } from "../PipelineStages.types"; -import { createLineTarget } from "../modifiers/scopeHandlers"; import type { LineTarget } from "../targets"; +import { createLineTarget } from "../targets"; export class LineNumberStage implements MarkStage { constructor(private mark: LineNumberMark) {} diff --git a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts index 9cd24e64b2..eae2fd0536 100644 --- a/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts +++ b/packages/cursorless-engine/src/processTargets/modifiers/scopeHandlers/LineScopeHandler.ts @@ -3,9 +3,8 @@ import type { Position, ScopeType, TextEditor, - TextLine, } from "@cursorless/common"; -import { LineTarget } from "../../targets"; +import { createLineTarget } from "../../targets"; import { BaseScopeHandler } from "./BaseScopeHandler"; import type { TargetScope } from "./scope.types"; @@ -61,19 +60,3 @@ function lineNumberToScope( ], }; } - -export function createLineTarget( - editor: TextEditor, - isReversed: boolean, - line: TextLine, - useFullRange = false, -) { - return new LineTarget({ - editor, - isReversed, - contentRange: - useFullRange || line.rangeTrimmed == null - ? line.range - : line.rangeTrimmed, - }); -} diff --git a/packages/cursorless-engine/src/processTargets/targets/LineTarget.ts b/packages/cursorless-engine/src/processTargets/targets/LineTarget.ts index a34c8be7af..7905c887db 100644 --- a/packages/cursorless-engine/src/processTargets/targets/LineTarget.ts +++ b/packages/cursorless-engine/src/processTargets/targets/LineTarget.ts @@ -1,4 +1,4 @@ -import type { TextEditor } from "@cursorless/common"; +import type { TextEditor, TextLine } from "@cursorless/common"; import { Position, Range, toLineRange } from "@cursorless/common"; import type { TextualType } from "../../typings/target.types"; import { expandToFullLine } from "../../util/rangeUtils"; @@ -94,3 +94,19 @@ export function constructLineTarget( ): LineTarget | undefined { return tryConstructTarget(LineTarget, editor, range, isReversed); } + +export function createLineTarget( + editor: TextEditor, + isReversed: boolean, + line: TextLine, + useFullRange = false, +) { + return new LineTarget({ + editor, + isReversed, + contentRange: + useFullRange || line.rangeTrimmed == null + ? line.range + : line.rangeTrimmed, + }); +}