Skip to content

Commit

Permalink
fix(buffer, change): save and restore local marks (#1439)
Browse files Browse the repository at this point in the history
* refactor(change): use built-in `apply_text_edits` to sync document changes

Use nvim's built-in `vim.lsp.util.apply_text_edits` to synchronize document changes.
Due to the built-in function already handling marks, it indirectly fixed #543
the issue of missing marks

* ci: add test, rename jumplist to jumping

* chore(test): add delay for "Cursor is ok for incsearch after scroll"

Annoying...

* feat(buffer, change): save and restore local marks
  • Loading branch information
xiyaowong committed Sep 17, 2023
1 parent 3b60650 commit 9e194a0
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 15 deletions.
39 changes: 39 additions & 0 deletions runtime/lua/vscode-neovim/api.lua
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
local api = vim.api
local fn = vim.fn
-- used to execute vscode command
local command_event_name = "vscode-command"
-- used for extension communications
Expand Down Expand Up @@ -77,4 +79,41 @@ function M.delete_buffers(bufs)
end
end

---Handle document changes
---@param bufnr number
---@param changes (string | integer)[][]
---@return number: changed tick of the buffer
function M.handle_changes(bufnr, changes)
-- Save and restore local marks
-- Code modified from https://github.com/neovim/neovim/pull/14630
local marks = {}
for _, m in pairs(fn.getmarklist(bufnr or api.nvim_get_current_buf())) do
if m.mark:match("^'[a-z]$") then
marks[m.mark:sub(2, 2)] = { m.pos[2], m.pos[3] - 1 } -- api-indexed
end
end

for _, change in ipairs(changes) do
api.nvim_buf_set_text(bufnr, unpack(change))
end

local max = api.nvim_buf_line_count(bufnr)
-- no need to restore marks that still exist
for _, m in pairs(fn.getmarklist(bufnr or api.nvim_get_current_buf())) do
marks[m.mark:sub(2, 2)] = nil
end
-- restore marks
for mark, pos in pairs(marks) do
if pos then
-- make sure we don't go out of bounds
local line = (api.nvim_buf_get_lines(bufnr, pos[1] - 1, pos[1], false))[1] or ""
pos[1] = math.min(pos[1], max)
pos[2] = math.min(pos[2], #line)
api.nvim_buf_set_mark(bufnr or 0, mark, pos[1], pos[2], {})
end
end

return api.nvim_buf_get_changedtick(bufnr)
end

return M
26 changes: 12 additions & 14 deletions src/document_change_manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -493,31 +493,29 @@ export class DocumentChangeManager implements Disposable, NeovimExtensionRequest
}
}

const requests: [string, unknown[]][] = [];

for (const c of contentChanges) {
const start = c.range.start;
const end = c.range.end;
const text = c.text;
const changeArgs = [];
for (const change of contentChanges) {
const {
text,
range: { start, end },
} = change;
const startBytes = convertCharNumToByteNum(origText.split(eol)[start.line], start.character);
const endBytes = convertCharNumToByteNum(origText.split(eol)[end.line], end.character);
requests.push(["nvim_buf_set_text", [bufId, start.line, startBytes, end.line, endBytes, text.split(eol)]]);
changeArgs.push([start.line, startBytes, end.line, endBytes, text.split(eol)]);
}

const bufTick: number = await this.client.request("nvim_buf_get_changedtick", [bufId]);
if (!bufTick) {
this.logger.warn(`${LOG_PREFIX}: Can't get changed tick for bufId: ${bufId}, deleted?`);
return;
}
this.logger.debug(
`${LOG_PREFIX}: BufId: ${bufId}, lineChanges: ${requests.length}, tick: ${bufTick}, skipTick: ${
bufTick + requests.length
}`,
);
this.bufferSkipTicks.set(bufId, bufTick + contentChanges.length);

this.bufferSkipTicks.set(bufId, bufTick + changeArgs.length);

this.logger.debug(`${LOG_PREFIX}: Setting wantInsertCursorUpdate to false`);
this.main.cursorManager.wantInsertCursorUpdate = false;
if (requests.length) await callAtomic(this.client, requests, this.logger, LOG_PREFIX);

const code = "return require('vscode-neovim.api').handle_changes(...)";
await this.client.executeLua(code, [bufId, changeArgs]);
};
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import path from "path";

import { NeovimClient } from "neovim";
import { Position } from "vscode";

import {
attachTestNvimClient,
Expand All @@ -13,7 +14,7 @@ import {
wait,
} from "../utils";

describe("Jumplist & jump actions", () => {
describe("Jumplist & jump actions & marks", () => {
// abc
let client: NeovimClient;
before(async () => {
Expand Down Expand Up @@ -73,4 +74,17 @@ describe("Jumplist & jump actions", () => {
client,
);
});

it("lower-case marks should still exist after changes #543", async function () {
const editor = await openTextDocument({ content: ["abc", "def", "uvw", "xyz"].join("\n") });
await wait(300);
await sendNeovimKeys(client, "maG");
editor.edit((builder) => {
builder.replace(new Position(0, 0), "ABC");
});
await wait(300);
await sendNeovimKeys(client, "'a");
await wait(300);
await assertContent({ cursor: [0, 0] }, client, editor);
});
});
2 changes: 2 additions & 0 deletions src/test/suite/vscode-integration-specific.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,9 @@ describe("VSCode integration specific stuff", () => {
const e = await openTextDocument(path.join(__dirname, "../../../test_fixtures/incsearch-scroll.ts"));

await sendVSCodeKeys("gg");
await wait(500);
await sendVSCodeKeys("/bla");
await wait(500);
await assertContent({ cursor: [115, 19] }, client);
assert.ok(e.visibleRanges[0].start.line <= 115);
});
Expand Down

0 comments on commit 9e194a0

Please sign in to comment.