Skip to content

Commit

Permalink
feat: re-add buffer subscription + first fixture test working!
Browse files Browse the repository at this point in the history
  • Loading branch information
Cedric Halbronn committed Mar 27, 2024
1 parent 0af0f57 commit aae0fc0
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 45 deletions.
10 changes: 4 additions & 6 deletions packages/cursorless-neovim/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import { injectCommandApi } from "./singletons/cmdapi.singleton";
import { NeovimCommandServerApi } from "./NeovimCommandServerApi";
import { constructTestHelpers } from "./constructTestHelpers";
import { injectCursorlessApi } from "./singletons/cursorlessapi.singleton";
// import { runRecordedTestCases } from "./suite/recorded.vscode.test";
import { runRecordedTestCases } from "./suite/recorded.vscode.test";

/**
* This function is called from talon.nvim to initialize the Cursorless engine.
Expand All @@ -38,8 +38,6 @@ export async function activate(context: NeovimExtensionContext) {

const { neovimIDE, hats, fileSystem } = await createNeovimIde(context);

//await subscribeBufferUpdates();

const normalizedIde =
neovimIDE.runMode === "production"
? neovimIDE
Expand Down Expand Up @@ -101,9 +99,9 @@ export async function activate(context: NeovimExtensionContext) {

console.warn("activate(): Cursorless extension loaded");

// console.warn("activate(): running the recorded test cases...");
// await runRecordedTestCases();
// console.warn("activate(): recorded test cases done");
console.warn("activate(): running the recorded test cases...");
await runRecordedTestCases();
console.warn("activate(): recorded test cases done");
}

async function createNeovimIde(context: ExtensionContext) {
Expand Down
89 changes: 77 additions & 12 deletions packages/cursorless-neovim/src/ide/neovim/NeovimEvents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,20 @@
// import { bufferManager } from "../../singletons/bufmgr.singleton";
import {
Disposable,
NormalizedIDE,
Position,
Range,
SpyIDE,
TextDocument,
TextDocumentChangeEvent,
TextDocumentContentChangeEvent,
TextEditor,
} from "@cursorless/common";

import { Buffer } from "neovim";
import { eventEmitter } from "../../events";
import { NeovimIDE } from "./NeovimIDE";
import { ide } from "@cursorless/cursorless-engine";

export function neovimOnDidChangeTextDocument(
listener: (event: TextDocumentChangeEvent) => void,
Expand All @@ -28,25 +34,84 @@ export function neovimOnDidChangeTextDocument(
return dummyEvent();
}

/**
* @see https://neovim.io/doc/user/api.html#api-buffer-updates
*/
// TODO: wrap all the arguments into a neovim.TextDocumentContentChangeEvent?
export async function receivedBufferEvent(
buffer: Buffer,
tick: number,
firstLine: number,
lastLine: number,
linedata: string[],
more: boolean,
): Promise<void> {
console.warn(
`BufferManager.receivedBufferEvent(): buffer.id=${buffer.id}, tick=${tick}, firstLine=${firstLine}, lastLine=${lastLine}, linedata=${linedata}, more=${more}`,
);

const ide_ = ide();
// TODO: It there a clean way to do it? Yes once we support pure dependency injection
// also we can make this function a method of NeovimIDE class
let neovimIDE: NeovimIDE;
if (ide_ instanceof NeovimIDE) {
neovimIDE = ide_;
} else if (ide_ instanceof NormalizedIDE) {
neovimIDE = ide_.original as NeovimIDE;
} else if (ide_ instanceof SpyIDE) {
const normalizedIDE = ide_.original as NormalizedIDE;
neovimIDE = normalizedIDE.original as NeovimIDE;
} else {
throw Error("receivedBufferEvent(): ide() is not NeovimIDE");
}
// We will need to get the document according to the buffer id
// once we want to support several windows
//const document = getTextDocumentForBufferId(buffer.id);
// But for now we get the current document
const document = (neovimIDE.activeTextEditor as TextEditor).document;

// const contents = await document.getText();
// console.warn(
// `BufferManager.receivedBufferEvent(): document.uri=${document.uri}, contents (before):\n${contents}\n`,
// );
eventEmitter.emit("onDidChangeTextDocument", {
document: document,
contentChanges: fromNeovimContentChange(
document,
buffer,
firstLine,
lastLine,
linedata,
),
// reason: fromNeovimReason(...),
});
}

export function fromNeovimContentChange(
document: TextDocument,
buffer: Buffer,
firstLine: number,
lastLine: number,
linedata: string[],
): TextDocumentContentChangeEvent[] {
const result = [];
// we can't just subtract lastLine - firstLine
for (let i = 0; i < linedata.length; ++i) {
const line = firstLine + i;
const text = linedata[i];
console.warn(`fromNeovimContentChange(): line=${line}, text=${text}`);
result.push({
range: new Range(new Position(line, 0), new Position(line, text.length)),
rangeOffset: 0,
rangeLength: 0,
text: text,
});
}
const text = linedata.join("\n");
console.warn(
`fromNeovimContentChange(): document.getText(): ${document.getText()}`,
);
// TODO: support when firstLine === lastLine
const range = new Range(
new Position(firstLine, 0),
new Position(lastLine - 1, document.lineAt(lastLine - 1).text.length),
);
const rangeOffset = document.offsetAt(range.start);
const rangeLength = document.offsetAt(range.end) - rangeOffset;
result.push({
range: range,
rangeOffset: rangeOffset,
rangeLength: rangeLength,
text: text,
});
return result;
}

Expand Down
18 changes: 9 additions & 9 deletions packages/cursorless-neovim/src/ide/neovim/NeovimIDE.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import { NeovimTextEditorImpl } from "./NeovimTextEditorImpl";
import { NeovimExtensionContext } from "./NeovimExtensionContext";
import { getTalonNvimPath } from "../../neovimApi";
import path from "path";
import { neovimOnDidChangeTextDocument } from "./NeovimEvents";

export class NeovimIDE implements IDE {
readonly configuration: NeovimConfiguration;
Expand Down Expand Up @@ -185,15 +186,14 @@ export class NeovimIDE implements IDE {
throw new Error("executeCommand Method not implemented.");
}

onDidChangeTextDocument: Event<TextDocumentChangeEvent> = dummyEvent;
// TODO: code below was tested successfully so can be reenabled when needed
// public onDidChangeTextDocument(
// listener: (event: TextDocumentChangeEvent) => void,
// ): Disposable {
// // console.warn("onDidChangeTextDocument Not implemented");
// // throw Error("onDidChangeTextDocument Not implemented");
// return neovimOnDidChangeTextDocument(listener);
// }
// onDidChangeTextDocument: Event<TextDocumentChangeEvent> = dummyEvent;
public onDidChangeTextDocument(
listener: (event: TextDocumentChangeEvent) => void,
): Disposable {
// console.warn("onDidChangeTextDocument Not implemented");
// throw Error("onDidChangeTextDocument Not implemented");
return neovimOnDidChangeTextDocument(listener);
}

onDidOpenTextDocument: Event<TextDocument> = dummyEvent;
onDidCloseTextDocument: Event<TextDocument> = dummyEvent;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,15 +131,17 @@ export class NeovimTextDocumentImpl implements TextDocument {

public getText(range?: Range): string {
if (range === undefined) {
console.warn(`getText() range=undefined`);
console.warn(`getText(all)`);
if (this._cachedTextValue == null) {
this._cachedTextValue = this._lines.join(this._eol);
}
console.warn(`getText() returning cached value=${this._cachedTextValue}`);
console.warn(
`getText(all) returning cached value=${this._cachedTextValue}`,
);
return this._cachedTextValue;
} else {
console.warn(
`getText() range=(${range?.start.line},${range?.start.character}),(${range?.end.line},${range?.end.character})`,
`getText(range=(${range?.start.line},${range?.start.character}),(${range?.end.line},${range?.end.character}))`,
);
}

Expand Down Expand Up @@ -178,7 +180,7 @@ export class NeovimTextDocumentImpl implements TextDocument {
);

console.warn(
`getText() returning multiple lines ${resultLines.join(lineEnding)}`,
`getText() returning multiple lines: ${resultLines.join(lineEnding)}`,
);
return resultLines.join(lineEnding);
}
Expand Down
16 changes: 5 additions & 11 deletions packages/cursorless-neovim/src/neovimHelpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { ide } from "@cursorless/cursorless-engine";
import { NeovimTextEditorImpl } from "./ide/neovim/NeovimTextEditorImpl";
import { NeovimIDE } from "./ide/neovim/NeovimIDE";
import { NormalizedIDE, SpyIDE } from "@cursorless/common";
import { receivedBufferEvent } from "./ide/neovim/NeovimEvents";

/**
* Initialize the current editor (and current document).
Expand Down Expand Up @@ -56,29 +57,22 @@ export async function updateTextEditor(): Promise<NeovimTextEditorImpl> {

/**
* Subscribe to buffer updates, e.g. when the text changes.
*
*
* NOTE: keeping this code as it was previously tested and it works but need to see when it
* will be useful (to update hats?)
*/
export async function subscribeBufferUpdates() {
const client = neovimContext().client;

// update the document since it is needed before we can attach?
await updateTextEditor();

/**
* "attach" to Nvim buffers to subscribe to buffer update events.
* This is similar to TextChanged but more powerful and granular.
*
* @see https://neovim.io/doc/user/api.html#nvim_buf_attach()
*/
const buffers = await client.buffers;
// const buffers = await client.buffers;
const buffers = [await client.buffer];
buffers.forEach(
/* async */ (buf) => {
console.warn("listening for changes in buffer: ", buf.id);
// buf.listen("lines", receivedBufferEvent);
// NOTE: import code from BufferManager.ts into neovimIDE and uncomment the line above
console.warn(`listening for changes in buffer: ${buf.id}`);
buf.listen("lines", receivedBufferEvent);
},
);
}
Expand Down
3 changes: 2 additions & 1 deletion packages/cursorless-neovim/src/registerCommands.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CURSORLESS_COMMAND_ID, CursorlessCommandId } from "@cursorless/common";
import { commandApi } from "./singletons/cmdapi.singleton";
import { updateTextEditor } from "./neovimHelpers";
import { subscribeBufferUpdates, updateTextEditor } from "./neovimHelpers";

/**
* Handle the command received from the command-server Neovim extension
Expand All @@ -19,6 +19,7 @@ export function handleCommandInternal(...allArguments: any[]): Promise<any> {
[CURSORLESS_COMMAND_ID]: async (...args: unknown[]) => {
// try {
await updateTextEditor();
await subscribeBufferUpdates();
const result = await commandApi().runCommandSafe(...args);
// const result = ["hello world"]; // simulate the result of "bring <target>"
return result;
Expand Down
4 changes: 2 additions & 2 deletions packages/cursorless-neovim/src/suite/recorded.vscode.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,8 @@ export async function runRecordedTestCases() {
// runTest(path, spyIde!),
// );
// TODO: use the assetRoot instead of hardcoding the path
runTest(
"C:\\cursorless\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\marks\\takeRowFour.yml",
await runTest(
"C:\\cursorless\\packages\\cursorless-vscode-e2e\\src\\suite\\fixtures\\recorded\\marks\\clearRowTwoPastFour.yml",
spyIde!,
);
}
Expand Down

0 comments on commit aae0fc0

Please sign in to comment.