Skip to content

Commit 9a98d48

Browse files
Implement InMemoryTextEditor (#3214)
Used by TalonJS and unit tests
1 parent 0033c5c commit 9a98d48

61 files changed

Lines changed: 285 additions & 366 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

packages/common/src/ide/fake/FakeIDE.ts

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ import type { TextDocumentChangeEvent } from "../types/Events";
1111
import type { FlashDescriptor } from "../types/FlashDescriptor";
1212
import type { QuickPickOptions } from "../types/QuickPickOptions";
1313
import type {
14+
Emit,
1415
Event,
1516
TextEditorSelectionChangeEvent,
1617
TextEditorVisibleRangesChangeEvent,
1718
} from "../types/events.types";
1819
import type {
1920
Disposable,
20-
IDE,
21+
EmittableIDE,
2122
OpenUntitledTextDocumentOptions,
2223
RunMode,
2324
WorkspaceFolder,
@@ -28,7 +29,7 @@ import FakeConfiguration from "./FakeConfiguration";
2829
import FakeKeyValueStore from "./FakeKeyValueStore";
2930
import FakeMessages from "./FakeMessages";
3031

31-
export class FakeIDE implements IDE {
32+
export class FakeIDE implements EmittableIDE {
3233
configuration = new FakeConfiguration();
3334
keyValueStore = new FakeKeyValueStore();
3435
clipboard = new FakeClipboard();
@@ -47,15 +48,15 @@ export class FakeIDE implements IDE {
4748
}
4849

4950
async flashRanges(_flashDescriptors: FlashDescriptor[]): Promise<void> {
50-
// empty
51+
// Empty
5152
}
5253

5354
async setHighlightRanges(
5455
_highlightId: string | undefined,
5556
_editor: TextEditor,
5657
_ranges: GeneralizedRange[],
5758
): Promise<void> {
58-
// empty
59+
// Empty
5960
}
6061

6162
onDidOpenTextDocument: Event<TextDocument> = dummyEvent;
@@ -68,6 +69,10 @@ export class FakeIDE implements IDE {
6869
dummyEvent;
6970
onDidChangeTextDocument: Event<TextDocumentChangeEvent> = dummyEvent;
7071

72+
emitDidChangeTextDocument: Emit<TextDocumentChangeEvent> = dummyEmit;
73+
emitDidChangeTextEditorSelection: Emit<TextEditorSelectionChangeEvent> =
74+
dummyEmit;
75+
7176
mockAssetsRoot(_assetsRoot: string) {
7277
this.assetsRoot_ = _assetsRoot;
7378
}
@@ -151,7 +156,11 @@ export class FakeIDE implements IDE {
151156
function dummyEvent() {
152157
return {
153158
dispose() {
154-
// empty
159+
// Empty
155160
},
156161
};
157162
}
163+
164+
function dummyEmit() {
165+
// Empty
166+
}

packages/common/src/ide/inMemoryTextDocument/InMemoryTextDocument.ts renamed to packages/common/src/ide/inMemoryTextEditor/InMemoryTextDocument.ts

File renamed without changes.

packages/cursorless-everywhere-talon-core/src/ide/TalonJsEditor.ts renamed to packages/common/src/ide/inMemoryTextEditor/InMemoryTextEditor.ts

Lines changed: 84 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,79 @@
11
import type {
22
Edit,
33
EditableTextEditor,
4+
EmittableIDE,
45
GeneralizedRange,
5-
InMemoryTextDocument,
66
OpenLinkOptions,
77
Range,
88
RevealLineAt,
9-
Selection,
9+
SelectionOffsets,
1010
SetSelectionsOpts,
11+
TextDocument,
1112
TextEditor,
1213
TextEditorOptions,
1314
} from "@cursorless/common";
14-
import { selectionsEqual } from "@cursorless/common";
15-
import type { Talon } from "../types/talon.types";
16-
import { setSelections } from "./setSelections";
17-
import type { TalonJsIDE } from "./TalonJsIDE";
18-
import { talonJsPerformEdits } from "./talonJsPerformEdits";
19-
20-
export class TalonJsEditor implements EditableTextEditor {
21-
options: TextEditorOptions = {
22-
tabSize: 4,
23-
insertSpaces: true,
24-
};
25-
26-
isActive = true;
27-
28-
constructor(
29-
private talon: Talon,
30-
private ide: TalonJsIDE,
31-
public id: string,
32-
public document: InMemoryTextDocument,
33-
public visibleRanges: Range[],
34-
public selections: Selection[],
35-
) {}
15+
import { Selection, selectionsEqual } from "@cursorless/common";
16+
import { URI } from "vscode-uri";
17+
import { InMemoryTextDocument } from "./InMemoryTextDocument";
18+
19+
interface Params {
20+
ide: EmittableIDE;
21+
languageId?: string;
22+
content?: string;
23+
options?: TextEditorOptions;
24+
visibleRanges?: Range[];
25+
selections?: Selection[] | SelectionOffsets[];
26+
}
27+
28+
export class InMemoryTextEditor implements EditableTextEditor {
29+
private static nextId = 0;
30+
31+
private readonly ide: EmittableIDE;
32+
readonly id: string;
33+
readonly isActive = true;
34+
readonly document: InMemoryTextDocument;
35+
readonly options: TextEditorOptions;
36+
readonly visibleRanges: Range[];
37+
selections: Selection[];
38+
39+
constructor({
40+
ide,
41+
languageId = "plaintext",
42+
content = "",
43+
visibleRanges,
44+
selections,
45+
options,
46+
}: Params) {
47+
this.ide = ide;
48+
this.id = String(InMemoryTextEditor.nextId++);
49+
const uri = URI.parse(`InMemoryTextEditor://${this.id}`);
50+
this.document = new InMemoryTextDocument(uri, languageId, content);
51+
52+
if (visibleRanges != null) {
53+
if (visibleRanges.length === 0) {
54+
throw new Error("Visible ranges must be non-empty");
55+
}
56+
this.visibleRanges = visibleRanges;
57+
} else {
58+
this.visibleRanges = [this.document.range];
59+
}
60+
61+
if (selections != null) {
62+
if (selections.length === 0) {
63+
throw new Error("Selections must be non-empty");
64+
}
65+
this.selections = selections.map((s) => {
66+
return s instanceof Selection ? s : createSelection(this.document, s);
67+
});
68+
} else {
69+
this.selections = [new Selection(0, 0, 0, 0)];
70+
}
71+
72+
this.options = options ?? {
73+
insertSpaces: true,
74+
tabSize: 4,
75+
};
76+
}
3677

3778
isEqual(other: TextEditor): boolean {
3879
return this.id === other.id;
@@ -42,14 +83,24 @@ export class TalonJsEditor implements EditableTextEditor {
4283
selections: Selection[],
4384
_opts?: SetSelectionsOpts,
4485
): Promise<void> {
86+
if (selections.length === 0) {
87+
throw new Error("Selections must be non-empty");
88+
}
4589
if (!selectionsEqual(this.selections, selections)) {
46-
await setSelections(this.talon, this.document, selections);
4790
this.selections = selections;
91+
this.ide.emitDidChangeTextEditorSelection({
92+
textEditor: this,
93+
selections: selections,
94+
});
4895
}
4996
}
5097

5198
edit(edits: Edit[]): Promise<boolean> {
52-
talonJsPerformEdits(this.talon, this.ide, this.document, edits);
99+
const changes = this.document.edit(edits);
100+
this.ide.emitDidChangeTextDocument({
101+
document: this.document,
102+
contentChanges: changes,
103+
});
53104
return Promise.resolve(true);
54105
}
55106

@@ -179,3 +230,10 @@ export class TalonJsEditor implements EditableTextEditor {
179230
throw Error("gitUnstageRange: not implemented");
180231
}
181232
}
233+
234+
function createSelection(document: TextDocument, selection: SelectionOffsets) {
235+
return new Selection(
236+
document.positionAt(selection.anchor),
237+
document.positionAt(selection.active),
238+
);
239+
}

packages/common/src/ide/inMemoryTextDocument/InMemoryTextLine.ts renamed to packages/common/src/ide/inMemoryTextEditor/InMemoryTextLine.ts

File renamed without changes.

packages/common/src/ide/inMemoryTextDocument/performEdits.ts renamed to packages/common/src/ide/inMemoryTextEditor/performEdits.ts

File renamed without changes.

packages/common/src/ide/inMemoryTextDocument/test/InMemoryTextDocument.test.ts renamed to packages/common/src/ide/inMemoryTextEditor/test/InMemoryTextDocument.test.ts

File renamed without changes.

packages/common/src/ide/inMemoryTextDocument/test/InMemoryTextDocumentEdit.test.ts renamed to packages/common/src/ide/inMemoryTextEditor/test/InMemoryTextDocumentEdit.test.ts

File renamed without changes.

packages/common/src/ide/inMemoryTextDocument/test/InMemoryTextDocumentLineAt.test.ts renamed to packages/common/src/ide/inMemoryTextEditor/test/InMemoryTextDocumentLineAt.test.ts

File renamed without changes.

packages/common/src/ide/inMemoryTextDocument/test/createTestDocument.ts renamed to packages/common/src/ide/inMemoryTextEditor/test/createTestDocument.ts

File renamed without changes.

packages/common/src/ide/types/events.types.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,13 @@ export interface Event<T> {
2929
): Disposable;
3030
}
3131

32+
/**
33+
* Represents a function that emits an event of type T.
34+
*/
35+
export interface Emit<T> {
36+
(event: T): void;
37+
}
38+
3239
/**
3340
* Represents an event describing the change in a {@link TextEditor.selections text editor's selections}.
3441
*/

0 commit comments

Comments
 (0)