Upstream vscode-neovim/vscode-neovim state of the touched files before 6a6303a21d
This commit is contained in:
commit
c0394e311c
2 files changed
+457
No files matched your search
@@ -0,0 +1,168 @@
|
||||
local M = {}
|
||||
|
||||
local fn, api = vim.fn, vim.api
|
||||
|
||||
function M.is_visual_mode()
|
||||
local mode = api.nvim_get_mode().mode
|
||||
return mode == "v" or mode == "V" or mode == "\x16"
|
||||
end
|
||||
|
||||
function M.get_char_at(line, byte_col, buf)
|
||||
if not buf or buf == 0 then
|
||||
buf = api.nvim_get_current_buf()
|
||||
end
|
||||
local line_str = fn.getbufoneline(buf, line)
|
||||
local char_idx = fn.charidx(line_str, (byte_col - 1))
|
||||
local char_nr = fn.strgetchar(line_str, char_idx)
|
||||
if char_nr ~= -1 then
|
||||
return fn.nr2char(char_nr)
|
||||
else
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
--- Gets the zero-indexed lines from the given buffer.
|
||||
---
|
||||
---@param bufnr integer bufnr to get the lines from
|
||||
---@param rows integer[] zero-indexed line numbers
|
||||
---@return table<integer, string> a table mapping rows to lines
|
||||
function M.get_lines(bufnr, rows)
|
||||
rows = type(rows) == "table" and rows or { rows }
|
||||
|
||||
local lines = {}
|
||||
for _, row in ipairs(rows) do
|
||||
lines[row] = (api.nvim_buf_get_lines(bufnr, row, row + 1, false) or { "" })[1]
|
||||
end
|
||||
return lines
|
||||
end
|
||||
|
||||
--- Gets the zero-indexed line from the given buffer.
|
||||
---
|
||||
---@param bufnr integer
|
||||
---@param row integer zero-indexed line number
|
||||
---@return string the line at row in filename
|
||||
function M.get_line(bufnr, row)
|
||||
return M.get_lines(bufnr, { row })[row]
|
||||
end
|
||||
|
||||
local str_utf16index = fn.has("nvim-0.11") == 1
|
||||
and function(text, byte_col)
|
||||
return vim.str_utfindex(text, "utf-16", byte_col)
|
||||
end
|
||||
or function(text, byte_col)
|
||||
return select(2, vim.str_utfindex(text, byte_col))
|
||||
end
|
||||
|
||||
--- VSCode counts a position in UTF-16 code units, Nvim counts bytes.
|
||||
---
|
||||
---@param text string the line the column belongs to
|
||||
---@param byte_col integer zero-indexed byte column, clamped to `text`
|
||||
---@return integer zero-indexed UTF-16 column
|
||||
function M.utf16_col(text, byte_col)
|
||||
byte_col = math.max(0, math.min(byte_col, #text))
|
||||
return byte_col == 0 and 0 or str_utf16index(text, byte_col)
|
||||
end
|
||||
|
||||
---@param buf integer
|
||||
---@param line integer one-indexed line
|
||||
---@param byte_col integer zero-indexed byte column
|
||||
---@return lsp.Position
|
||||
function M.lsp_position(buf, line, byte_col)
|
||||
return { line = line - 1, character = M.utf16_col(M.get_line(buf, line - 1) or "", byte_col) }
|
||||
end
|
||||
|
||||
--- Converts an inclusive Nvim byte range to an exclusive LSP range.
|
||||
---
|
||||
--- The end stops at the line end, so that a linewise range - which passes the whole
|
||||
--- line length as its end column - stays a valid position instead of running past it.
|
||||
---
|
||||
---@param buf integer
|
||||
---@param start_line integer one-indexed
|
||||
---@param start_col integer zero-indexed byte column
|
||||
---@param end_line integer one-indexed
|
||||
---@param end_col integer zero-indexed byte column, inclusive
|
||||
---@return lsp.Range
|
||||
function M.lsp_range(buf, start_line, start_col, end_line, end_col)
|
||||
local end_text = M.get_line(buf, end_line - 1) or ""
|
||||
local end_char = M.utf16_col(end_text, end_col)
|
||||
if vim.o.selection ~= "exclusive" then
|
||||
end_char = math.min(end_char + 1, M.utf16_col(end_text, #end_text))
|
||||
end
|
||||
return {
|
||||
start = M.lsp_position(buf, start_line, start_col),
|
||||
["end"] = { line = end_line - 1, character = end_char },
|
||||
}
|
||||
end
|
||||
|
||||
--- Compare two positions
|
||||
---@param a lsp.Position
|
||||
---@param b lsp.Position
|
||||
---@return -1|0|1 -1 if a < b, 0 if a == b, 1 if a > b
|
||||
function M.compare_position(a, b)
|
||||
if a.line > b.line then
|
||||
return 1
|
||||
end
|
||||
if a.line == b.line and a.character > b.character then
|
||||
return 1
|
||||
end
|
||||
if a.line == b.line and a.character == b.character then
|
||||
return 0
|
||||
end
|
||||
return -1
|
||||
end
|
||||
|
||||
---Debounce a function.
|
||||
---@param func function function to debounce
|
||||
---@param time number trialing time in ms
|
||||
---@return function
|
||||
function M.debounce(func, time)
|
||||
local timer
|
||||
return function(...)
|
||||
local args = { ... }
|
||||
if timer and timer:is_active() then
|
||||
timer:close()
|
||||
end
|
||||
timer = vim.defer_fn(function()
|
||||
func(unpack(args))
|
||||
end, time)
|
||||
end
|
||||
end
|
||||
|
||||
-- Since Nvim 0.10.0, `virtcol2col` changed from returning the last byte of a
|
||||
-- multi-byte character to returning the first byte. However, we need the column
|
||||
-- of the last byte, which is consistent with the selected region in Nvim.
|
||||
-- See https://github.com/neovim/neovim/issues/29786
|
||||
if fn.has("nvim-0.10.0") == 0 then
|
||||
M.virtcol2col = fn.virtcol2col
|
||||
else
|
||||
---@diagnostic disable-next-line: duplicate-set-field
|
||||
M.virtcol2col = function(winid, lnum, virtcol)
|
||||
local byte_idx = fn.virtcol2col(winid, lnum, virtcol) - 1
|
||||
local buf = api.nvim_win_get_buf(winid)
|
||||
local line = M.get_line(buf, lnum - 1)
|
||||
local char_idx = fn.charidx(line, byte_idx)
|
||||
local prefix = fn.strcharpart(line, 0, char_idx + 1)
|
||||
return #prefix
|
||||
end
|
||||
end
|
||||
|
||||
-- Wrapper for nvim_set_option_value that sets the option only if the value differs
|
||||
function M.set_option_value(name, value, opts)
|
||||
opts = opts or {}
|
||||
local current = api.nvim_get_option_value(name, opts)
|
||||
if current ~= value then
|
||||
api.nvim_set_option_value(name, value, opts)
|
||||
end
|
||||
end
|
||||
|
||||
-- Wrapper for set_option_value to set a buffer option
|
||||
function M.buf_set_option(buf, name, value)
|
||||
return M.set_option_value(name, value, { buf = buf })
|
||||
end
|
||||
|
||||
-- Wrapper for set_option_value to set a window option
|
||||
function M.win_set_option(win, name, value)
|
||||
return M.set_option_value(name, value, { win = win })
|
||||
end
|
||||
|
||||
return M
|
||||
@@ -0,0 +1,289 @@
|
||||
import { NeovimClient } from "neovim";
|
||||
|
||||
import {
|
||||
attachTestNvimClient,
|
||||
sendVSCodeKeys,
|
||||
assertContent,
|
||||
closeAllActiveEditors,
|
||||
closeNvimClient,
|
||||
setCursor,
|
||||
sendEscapeKey,
|
||||
openTextDocument,
|
||||
sendInsertKey,
|
||||
} from "./integrationUtils";
|
||||
|
||||
describe("Multi-width characters", () => {
|
||||
let client: NeovimClient;
|
||||
before(async () => {
|
||||
client = await attachTestNvimClient();
|
||||
});
|
||||
after(async () => {
|
||||
await closeNvimClient(client);
|
||||
await closeAllActiveEditors();
|
||||
});
|
||||
|
||||
it("Works - 2col width chars", async () => {
|
||||
await openTextDocument({ content: ["测试微服务", "", "没办法跳转到最后一个"].join("\n") });
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
content: ["测试微服务", "", "没办法跳转到最后一个"],
|
||||
cursor: [0, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("x", 500);
|
||||
await assertContent(
|
||||
{
|
||||
content: ["测试服务", "", "没办法跳转到最后一个"],
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
// await sendVSCodeKeys("vll");
|
||||
// await assertContent(
|
||||
// {
|
||||
// vsCodeSelections: [new vscode.Selection(0, 2, 0, 4)],
|
||||
// },
|
||||
// client,
|
||||
// );
|
||||
|
||||
await setCursor(2, 5);
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [2, 5],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Works - 1col-2byte width chars", async () => {
|
||||
await openTextDocument({ content: ["żżżżżżżż',", "ńńńńńńńń',"].join("\n") });
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
content: ["żżżżżżżż',", "ńńńńńńńń',"],
|
||||
cursor: [0, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendVSCodeKeys("lll");
|
||||
await assertContent({ vsCodeCursor: [0, 3] }, client);
|
||||
|
||||
await sendVSCodeKeys("x");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["żżżżżżż',", "ńńńńńńńń',"],
|
||||
vsCodeCursor: [0, 3],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Works - 1col-3byte width chars", async () => {
|
||||
await openTextDocument({ content: ["1ᵩᵩ123"].join("\n") });
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
content: ["1ᵩᵩ123"],
|
||||
cursor: [0, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent({ vsCodeCursor: [0, 2] }, client);
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent({ vsCodeCursor: [0, 4] }, client);
|
||||
|
||||
await sendVSCodeKeys("x");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["1ᵩᵩ13"],
|
||||
vsCodeCursor: [0, 4],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Cursor is ok after exiting insert mode - 2 col chars", async () => {
|
||||
await openTextDocument({ content: ["测试微服务", "", "没办法跳转到最后一个"].join("\n") });
|
||||
|
||||
await sendVSCodeKeys("lll");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 3],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendInsertKey();
|
||||
|
||||
await sendEscapeKey();
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Cursor is ok after exiting insert mode - 1col-2byte chars", async () => {
|
||||
await openTextDocument({ content: ["żżżżżżżż',", "ńńńńńńńń',"].join("\n") });
|
||||
|
||||
await sendVSCodeKeys("lll");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 3],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendInsertKey();
|
||||
|
||||
await sendEscapeKey();
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Cursor is ok after exiting insert mode - 1col-3byte width chars", async () => {
|
||||
await openTextDocument({ content: ["1ᵩᵩ123"].join("\n") });
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
content: ["1ᵩᵩ123"],
|
||||
cursor: [0, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent({ vsCodeCursor: [0, 2] }, client);
|
||||
await sendInsertKey("a");
|
||||
|
||||
await sendEscapeKey();
|
||||
await assertContent({ vsCodeCursor: [0, 2] }, client);
|
||||
|
||||
await sendVSCodeKeys("ll");
|
||||
await sendInsertKey("a");
|
||||
await sendEscapeKey();
|
||||
await assertContent({ vsCodeCursor: [0, 4] }, client);
|
||||
});
|
||||
|
||||
it("Cursor is ok after exiting insert mode at end of the line - 1col-3byte width chars", async () => {
|
||||
await openTextDocument({ content: "ᵩ123" });
|
||||
|
||||
await sendInsertKey("A");
|
||||
await sendEscapeKey();
|
||||
await assertContent({ vsCodeCursor: [0, 3] }, client);
|
||||
});
|
||||
|
||||
it("Multi byte with tabs", async () => {
|
||||
await openTextDocument({ content: "\t\t测试\t微服务" });
|
||||
|
||||
await sendVSCodeKeys("gg0");
|
||||
await sendVSCodeKeys("l");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 1],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("l");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("x");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["\t\t试\t微服务"],
|
||||
vsCodeCursor: [0, 2],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 4],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("x");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["\t\t试\t服务"],
|
||||
vsCodeCursor: [0, 4],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
|
||||
it("Issue #503", async () => {
|
||||
await openTextDocument({ content: "ŷaŷbŷcŷd = functionŷ(par1)" });
|
||||
|
||||
await sendVSCodeKeys("f(l");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 26],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("ci(");
|
||||
await assertContent({ vsCodeCursor: [0, 26], content: ["ŷaŷbŷcŷd = functionŷ()"] }, client);
|
||||
});
|
||||
|
||||
it("Works - Emoji chars", async () => {
|
||||
await openTextDocument({ content: ["🚀🕵️💡🤣", "", "🕵️🕵️🕵️🕵️"].join("\n") });
|
||||
|
||||
await assertContent(
|
||||
{
|
||||
content: ["🚀🕵️💡🤣", "", "🕵️🕵️🕵️🕵️"],
|
||||
cursor: [0, 0],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("ll");
|
||||
await assertContent(
|
||||
{
|
||||
vsCodeCursor: [0, 5],
|
||||
},
|
||||
client,
|
||||
);
|
||||
|
||||
await sendVSCodeKeys("x");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["🚀🕵️🤣", "", "🕵️🕵️🕵️🕵️"],
|
||||
vsCodeCursor: [0, 5],
|
||||
},
|
||||
client,
|
||||
);
|
||||
await sendVSCodeKeys("jjdw");
|
||||
await assertContent(
|
||||
{
|
||||
content: ["🚀🕵️🤣", "", "🕵️🕵️"],
|
||||
vsCodeCursor: [2, 3],
|
||||
},
|
||||
client,
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user