fix(buffer): keep the nvim cursor when nvim switches to another document
When nvim moves to a buffer whose document is not shown (e.g. a jump to a global mark), vscode shows the document with the selection it saved last time, and the layout sync then sends that selection back to nvim, overwriting the position nvim jumped to. Read the cursor of the current nvim window before showing the document and apply it to the editor, so the layout sync sends nvim its own cursor. Fixes #2699
This commit is contained in:
1 parent
6e63acc87e
commit
ea18b8e453
2 files changed
+54
-3
No files matched your search
+34
-1
@@ -10,6 +10,7 @@ import {
|
|||||||
EventEmitter,
|
EventEmitter,
|
||||||
LogLevel,
|
LogLevel,
|
||||||
NotebookDocument,
|
NotebookDocument,
|
||||||
|
Position,
|
||||||
ProgressLocation,
|
ProgressLocation,
|
||||||
Selection,
|
Selection,
|
||||||
TextDocument,
|
TextDocument,
|
||||||
@@ -30,7 +31,15 @@ import { config } from "./config";
|
|||||||
import { EventBusData, eventBus } from "./eventBus";
|
import { EventBusData, eventBus } from "./eventBus";
|
||||||
import { createLogger } from "./logger";
|
import { createLogger } from "./logger";
|
||||||
import { MainController } from "./main_controller";
|
import { MainController } from "./main_controller";
|
||||||
import { ManualPromise, Progress, convertByteNumToCharNum, disposeAll, fileExists, wait } from "./utils";
|
import {
|
||||||
|
ManualPromise,
|
||||||
|
Progress,
|
||||||
|
convertByteNumToCharNum,
|
||||||
|
convertVimPositionToEditorPosition,
|
||||||
|
disposeAll,
|
||||||
|
fileExists,
|
||||||
|
wait,
|
||||||
|
} from "./utils";
|
||||||
|
|
||||||
// NOTE: document and editors in vscode events and namespace are reference stable
|
// NOTE: document and editors in vscode events and namespace are reference stable
|
||||||
// Integration notes:
|
// Integration notes:
|
||||||
@@ -348,6 +357,9 @@ export class BufferManager implements Disposable {
|
|||||||
this.textDocumentToBufferId.set(doc, bufnr);
|
this.textDocumentToBufferId.set(doc, bufnr);
|
||||||
}
|
}
|
||||||
if (window.activeTextEditor?.document !== doc) {
|
if (window.activeTextEditor?.document !== doc) {
|
||||||
|
// Neovim moved to this buffer itself (e.g. a jump to a global mark), so its cursor
|
||||||
|
// must win over the selection vscode restores when showing the document again
|
||||||
|
const neovimCursor = await this.getNeovimCursorInBuffer(bufnr);
|
||||||
const editor = await window.showTextDocument(doc, {
|
const editor = await window.showTextDocument(doc, {
|
||||||
// viewColumn: vscode.ViewColumn.Active,
|
// viewColumn: vscode.ViewColumn.Active,
|
||||||
// !need to force editor to appear in the same column even if vscode 'revealIfOpen' setting is true
|
// !need to force editor to appear in the same column even if vscode 'revealIfOpen' setting is true
|
||||||
@@ -355,6 +367,11 @@ export class BufferManager implements Disposable {
|
|||||||
preserveFocus: false,
|
preserveFocus: false,
|
||||||
preview: false,
|
preview: false,
|
||||||
});
|
});
|
||||||
|
if (neovimCursor) {
|
||||||
|
const active = convertVimPositionToEditorPosition(editor, neovimCursor);
|
||||||
|
editor.selections = [new Selection(active, active)];
|
||||||
|
editor.revealRange(editor.selection, TextEditorRevealType.InCenterIfOutsideViewport);
|
||||||
|
}
|
||||||
// force resync
|
// force resync
|
||||||
this.onDidChangeEditorOptions(editor);
|
this.onDidChangeEditorOptions(editor);
|
||||||
}
|
}
|
||||||
@@ -363,6 +380,22 @@ export class BufferManager implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the (0, 0)-indexed byte cursor of the current neovim window, if it shows the given buffer
|
||||||
|
*/
|
||||||
|
private async getNeovimCursorInBuffer(bufId: number): Promise<Position | undefined> {
|
||||||
|
const cursor = (await this.client.lua(
|
||||||
|
`
|
||||||
|
local buf = ...
|
||||||
|
if vim.api.nvim_get_current_buf() == buf then
|
||||||
|
return vim.api.nvim_win_get_cursor(0)
|
||||||
|
end
|
||||||
|
`,
|
||||||
|
[bufId],
|
||||||
|
)) as [number, number] | null;
|
||||||
|
return cursor ? new Position(cursor[0] - 1, cursor[1]) : undefined;
|
||||||
|
}
|
||||||
|
|
||||||
private handleWindowChanged = async (winId: number, token: CancellationToken): Promise<void> => {
|
private handleWindowChanged = async (winId: number, token: CancellationToken): Promise<void> => {
|
||||||
logger.debug(`window changed, target window id: ${winId}`);
|
logger.debug(`window changed, target window id: ${winId}`);
|
||||||
if (winId === 1000) {
|
if (winId === 1000) {
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
|
import { strict as assert } from "assert";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { NeovimClient } from "neovim";
|
import { NeovimClient } from "neovim";
|
||||||
import { Position } from "vscode";
|
import { Position, window } from "vscode";
|
||||||
|
|
||||||
import { wait } from "../../utils";
|
import { wait, waitUntil } from "../../utils";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
attachTestNvimClient,
|
attachTestNvimClient,
|
||||||
@@ -88,4 +89,21 @@ describe("Jumplist & jump actions & marks", () => {
|
|||||||
await wait(300);
|
await wait(300);
|
||||||
await assertContent({ cursor: [0, 0] }, client, editor);
|
await assertContent({ cursor: [0, 0] }, client, editor);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("Global mark jump to another file keeps the mark position #2699", async () => {
|
||||||
|
const markEditor = await openTextDocument(path.join(__dirname, "../../../test_fixtures/scrolltest.txt"));
|
||||||
|
await sendVSCodeKeys("24G4lmA");
|
||||||
|
await assertContent({ cursor: [23, 4] }, client);
|
||||||
|
// Leave a different selection behind for VSCode to restore when the file is shown again
|
||||||
|
await sendVSCodeKeys("29G02l");
|
||||||
|
await assertContent({ cursor: [28, 2] }, client);
|
||||||
|
|
||||||
|
await openTextDocument(path.join(__dirname, "../../../test_fixtures/cursor-revealing.txt"));
|
||||||
|
await sendNeovimKeys(client, "`A");
|
||||||
|
await waitUntil(() => assert.equal(window.activeTextEditor?.document, markEditor.document));
|
||||||
|
|
||||||
|
// The restored selection would overwrite the mark position only after the editor is shown
|
||||||
|
await wait(1000);
|
||||||
|
await assertContent({ cursor: [23, 4] }, client);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
Reference in new issue
Block a user