Skip to content

Commit 4af66c9

Browse files
saidelikeCedric Halbronn
andauthored
refactor: make the text editor edit() interface better for neovim (#2270)
## Checklist - [ ] 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 ` 7189 passing (5m)` all tests passing. Just marking it as a draft PR for now until I confirm from neovim side that this is good interface. Co-authored-by: Cedric Halbronn <[email protected]>
1 parent 463a00f commit 4af66c9

File tree

12 files changed

+54
-80
lines changed

12 files changed

+54
-80
lines changed

packages/common/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ export * from "./types/RangeExpansionBehavior";
3535
export * from "./types/InputBoxOptions";
3636
export * from "./types/Position";
3737
export * from "./types/Range";
38+
export * from "./types/Edit";
3839
export * from "./types/RevealLineAt";
3940
export * from "./types/Selection";
4041
export * from "./types/TextDocument";

packages/common/src/types/Edit.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { Range } from "..";
2+
3+
/** Represent a single edit/change in the document */
4+
export interface Edit {
5+
range: Range;
6+
text: string;
7+
8+
/**
9+
* If this edit is an insertion, ie the range has zero length, then this
10+
* field can be set to `true` to indicate that any adjacent empty selection
11+
* should *not* be shifted to the right, as would normally happen with an
12+
* insertion. This is equivalent to the
13+
* [distinction](https://code.visualstudio.com/api/references/vscode-api#TextEditorEdit)
14+
* in a vscode edit builder between doing a replace with an empty range
15+
* versus doing an insert.
16+
*/
17+
isReplace?: boolean;
18+
}

packages/common/src/types/TextEditor.ts

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import type {
2+
Edit,
23
Position,
34
Range,
45
RevealLineAt,
56
Selection,
67
TextDocument,
7-
TextEditorEdit,
88
TextEditorOptions,
99
} from "..";
1010

@@ -81,18 +81,11 @@ export interface EditableTextEditor extends TextEditor {
8181
/**
8282
* Perform an edit on the document associated with this text editor.
8383
*
84-
* The given callback-function is invoked with an {@link TextEditorEdit edit-builder} which must
85-
* be used to make edits. Note that the edit-builder is only valid while the
86-
* callback executes.
87-
*
88-
* @param callback A function which can create edits using an {@link TextEditorEdit edit-builder}.
89-
* @param options The undo/redo behavior around this edit. By default, undo stops will be created before and after this edit.
84+
* @param edits the list of edits that need to be applied to the document
85+
* (note that the implementation might need to sort them in reverse order)
9086
* @return A promise that resolves with a value indicating if the edits could be applied.
9187
*/
92-
edit(
93-
callback: (editBuilder: TextEditorEdit) => void,
94-
options?: { undoStopBefore: boolean; undoStopAfter: boolean },
95-
): Promise<boolean>;
88+
edit(edits: Edit[]): Promise<boolean>;
9689

9790
/**
9891
* Edit a new new notebook cell above.

packages/cursorless-engine/src/actions/BreakLine.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
1-
import { FlashStyle, Position, Range, TextEditor } from "@cursorless/common";
1+
import {
2+
Edit,
3+
FlashStyle,
4+
Position,
5+
Range,
6+
TextEditor,
7+
} from "@cursorless/common";
28
import { flatten, zip } from "lodash";
39
import type { RangeUpdater } from "../core/updateSelections/RangeUpdater";
410
import { performEditsAndUpdateRanges } from "../core/updateSelections/updateSelections";
511
import { ide } from "../singletons/ide.singleton";
6-
import { Edit } from "../typings/Types";
712
import { Target } from "../typings/target.types";
813
import { flashTargets, runOnTargetsForEachEditor } from "../util/targetUtils";
914
import type { ActionReturnValue } from "./actions.types";

packages/cursorless-engine/src/actions/JoinLines.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
1-
import { FlashStyle, Range, TextEditor } from "@cursorless/common";
1+
import { Edit, FlashStyle, Range, TextEditor } from "@cursorless/common";
2+
import { range as iterRange, map, pairwise } from "itertools";
23
import { flatten, zip } from "lodash";
34
import type { RangeUpdater } from "../core/updateSelections/RangeUpdater";
45
import { performEditsAndUpdateRanges } from "../core/updateSelections/updateSelections";
56
import { ide } from "../singletons/ide.singleton";
6-
import { Edit } from "../typings/Types";
77
import { Target } from "../typings/target.types";
88
import { flashTargets, runOnTargetsForEachEditor } from "../util/targetUtils";
99
import type { ActionReturnValue } from "./actions.types";
10-
import { range as iterRange, map, pairwise } from "itertools";
1110

1211
export default class JoinLines {
1312
constructor(private rangeUpdater: RangeUpdater) {

packages/cursorless-engine/src/actions/Wrap.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import {
2+
Edit,
23
FlashStyle,
34
RangeExpansionBehavior,
45
Selection,
@@ -10,7 +11,6 @@ import {
1011
performEditsAndUpdateFullSelectionInfos,
1112
} from "../core/updateSelections/updateSelections";
1213
import { ide } from "../singletons/ide.singleton";
13-
import { Edit } from "../typings/Types";
1414
import { Target } from "../typings/target.types";
1515
import { FullSelectionInfo } from "../typings/updateSelections";
1616
import { setSelectionsWithoutFocusingEditor } from "../util/setSelectionsAndFocusEditor";

packages/cursorless-engine/src/core/updateSelections/RangeUpdater.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import type {
22
Disposable,
3+
Edit,
34
TextDocument,
45
TextDocumentChangeEvent,
56
TextDocumentContentChangeEvent,
67
} from "@cursorless/common";
78
import { pull } from "lodash";
89
import { ide } from "../../singletons/ide.singleton";
9-
import type { Edit } from "../../typings/Types";
1010
import {
1111
ExtendedTextDocumentChangeEvent,
1212
FullRangeInfo,

packages/cursorless-engine/src/core/updateSelections/updateSelections.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import {
2-
RangeExpansionBehavior,
2+
Edit,
33
EditableTextEditor,
44
Range,
5+
RangeExpansionBehavior,
56
Selection,
67
TextDocument,
78
} from "@cursorless/common";
89
import { flatten } from "lodash";
9-
import { Edit } from "../../typings/Types";
1010
import {
1111
FullSelectionInfo,
1212
SelectionInfo,

packages/cursorless-engine/src/typings/Types.ts

Lines changed: 1 addition & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Range, Selection, TextEditor } from "@cursorless/common";
1+
import type { Edit, Range, Selection, TextEditor } from "@cursorless/common";
22
import type { SyntaxNode } from "web-tree-sitter";
33

44
export interface SelectionWithEditor {
@@ -72,23 +72,6 @@ export type SelectionExtractor = (
7272
nodes: SyntaxNode,
7373
) => SelectionWithContext;
7474

75-
/** Represent a single edit/change in the document */
76-
export interface Edit {
77-
range: Range;
78-
text: string;
79-
80-
/**
81-
* If this edit is an insertion, ie the range has zero length, then this
82-
* field can be set to `true` to indicate that any adjacent empty selection
83-
* should *not* be shifted to the right, as would normally happen with an
84-
* insertion. This is equivalent to the
85-
* [distinction](https://code.visualstudio.com/api/references/vscode-api#TextEditorEdit)
86-
* in a vscode edit builder between doing a replace with an empty range
87-
* versus doing an insert.
88-
*/
89-
isReplace?: boolean;
90-
}
91-
9275
export interface EditWithRangeUpdater extends Edit {
9376
/**
9477
* This function will be passed the resulting range containing {@link text}

packages/cursorless-engine/src/util/performDocumentEdits.ts

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
1-
import { EditableTextEditor } from "@cursorless/common";
1+
import { Edit, EditableTextEditor } from "@cursorless/common";
22
import { RangeUpdater } from "../core/updateSelections/RangeUpdater";
3-
import { Edit } from "../typings/Types";
43

54
export async function performDocumentEdits(
65
rangeUpdater: RangeUpdater,
@@ -12,17 +11,7 @@ export async function performDocumentEdits(
1211
edits.filter((edit) => edit.isReplace),
1312
);
1413

15-
const wereEditsApplied = await editor.edit((editBuilder) => {
16-
edits.forEach(({ range, text, isReplace }) => {
17-
if (text === "") {
18-
editBuilder.delete(range);
19-
} else if (range.isEmpty && !isReplace) {
20-
editBuilder.insert(range.start, text);
21-
} else {
22-
editBuilder.replace(range, text);
23-
}
24-
});
25-
});
14+
const wereEditsApplied = await editor.edit(edits);
2615

2716
deregister();
2817

0 commit comments

Comments
 (0)