From 85e0d034feb30a613f898876ff1bb6ce589a2fca Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Fri, 10 Nov 2023 22:34:49 +0100 Subject: [PATCH 1/3] Don't get selection via buf_get_text, just cut and restore registers. Easier than taking care of all the stupid edge-cases, like multibyte-combined-characters or virtualedit. --- DOC.md | 4 +- lua/luasnip/config.lua | 5 +- lua/luasnip/init.lua | 1 + lua/luasnip/util/_builtin_vars.lua | 3 +- lua/luasnip/util/select.lua | 152 +++++++++++++++++++++++++ lua/luasnip/util/util.lua | 148 ------------------------ tests/integration/parser_spec.lua | 4 +- tests/integration/select_spec.lua | 176 +++++++++++++++++++++++++++++ 8 files changed, 340 insertions(+), 153 deletions(-) create mode 100644 lua/luasnip/util/select.lua create mode 100644 tests/integration/select_spec.lua diff --git a/DOC.md b/DOC.md index 257c6b333..bf27de9c2 100644 --- a/DOC.md +++ b/DOC.md @@ -3455,7 +3455,9 @@ These are the settings you can provide to `luasnip.setup()`: fine (alternatively, this can also be mapped using `luasnip-delete-check`). - `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related - variables (not set by default). + variables (not set by default). + If you want to set this mapping yourself, map `ls.select_keys` (not a + function, actually a string/key-combination) as a rhs. - `enable_autosnippets`: Autosnippets are disabled by default to minimize performance penalty if unused. Set to `true` to enable. - `ext_opts`: Additional options passed to extmarks. Can be used to add diff --git a/lua/luasnip/config.lua b/lua/luasnip/config.lua index 50ccddf3b..3a9ccb866 100644 --- a/lua/luasnip/config.lua +++ b/lua/luasnip/config.lua @@ -285,8 +285,9 @@ c = { if session.config.store_selection_keys then vim.cmd( string.format( - [[xnoremap %s :lua require('luasnip.util.util').store_selection()gv"_s]], - session.config.store_selection_keys + [[xnoremap %s %s]], + session.config.store_selection_keys, + require("luasnip.util.select").select_keys ) ) end diff --git a/lua/luasnip/init.lua b/lua/luasnip/init.lua index 7e0980425..3ec748af6 100644 --- a/lua/luasnip/init.lua +++ b/lua/luasnip/init.lua @@ -846,6 +846,7 @@ local ls_lazy = { config = function() return require("luasnip.config") end, multi_snippet = function() return require("luasnip.nodes.multiSnippet").new_multisnippet end, snippet_source = function() return require("luasnip.session.snippet_collection.source") end, + select_keys = function() return require("luasnip.util.select").select_keys end } ls = util.lazy_table({ diff --git a/lua/luasnip/util/_builtin_vars.lua b/lua/luasnip/util/_builtin_vars.lua index 928558a12..f1e44f2e8 100644 --- a/lua/luasnip/util/_builtin_vars.lua +++ b/lua/luasnip/util/_builtin_vars.lua @@ -1,4 +1,5 @@ local util = require("luasnip.util.util") +local select_util = require("luasnip.util.select") local lazy_vars = {} -- Variables defined in https://code.visualstudio.com/docs/editor/userdefinedsnippets#_variables @@ -156,7 +157,7 @@ local function eager_vars(info) vars.TM_LINE_INDEX = tostring(pos[1]) vars.TM_LINE_NUMBER = tostring(pos[1] + 1) vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT, vars.TM_SELECTED_TEXT = - util.get_selection() + select_util.retrieve() -- These are for backward compatibility, for now on all builtins that are not part of TM_ go in LS_ vars.SELECT_RAW, vars.SELECT_DEDENT = vars.LS_SELECT_RAW, vars.LS_SELECT_DEDENT diff --git a/lua/luasnip/util/select.lua b/lua/luasnip/util/select.lua new file mode 100644 index 000000000..c1011feef --- /dev/null +++ b/lua/luasnip/util/select.lua @@ -0,0 +1,152 @@ +local M = {} + +local SELECT_RAW = "LUASNIP_SELECT_RAW" +local SELECT_DEDENT = "LUASNIP_SELECT_DEDENT" +local TM_SELECT = "LUASNIP_TM_SELECT" + +function M.retrieve() + local ok, val = pcall(vim.api.nvim_buf_get_var, 0, SELECT_RAW) + if ok then + local result = { + val, + vim.api.nvim_buf_get_var(0, SELECT_DEDENT), + vim.api.nvim_buf_get_var(0, TM_SELECT), + } + + vim.api.nvim_buf_del_var(0, SELECT_RAW) + vim.api.nvim_buf_del_var(0, SELECT_DEDENT) + vim.api.nvim_buf_del_var(0, TM_SELECT) + + return unpack(result) + end + return {}, {}, {} +end + +local function get_min_indent(lines) + -- "^(%s*)%S": match only lines that actually contain text. + local min_indent = lines[1]:match("^(%s*)%S") + for i = 2, #lines do + -- %s* -> at least matches + local line_indent = lines[i]:match("^(%s*)%S") + -- ignore if not matched. + if line_indent then + -- if no line until now matched, use line_indent. + if not min_indent or #line_indent < #min_indent then + min_indent = line_indent + end + end + end + return min_indent +end + +local function store_registers(...) + local names = { ... } + local restore_data = {} + for _, name in ipairs(names) do + restore_data[name] = { + data = vim.fn.getreg(name), + type = vim.fn.getregtype(name), + } + end + return restore_data +end + +local function restore_registers(restore_data) + for name, name_restore_data in pairs(restore_data) do + vim.fn.setreg(name, name_restore_data.data, name_restore_data.type) + end +end + +-- subtle: `:lua` exits VISUAL, which means that the '< '>-marks will be set correctly! +-- Afterwards, we can just use lua, which does not change the mode. +M.select_keys = + [[:lua require("luasnip.util.select").pre_cut()gv"zslua require('luasnip.util.select').post_cut("z")]] + +local saved_registers +local lines +local start_line, start_col, end_line, end_col +local mode +function M.pre_cut() + -- store registers so we don't change any of them. + -- "" is affected since we perform a cut (s), 1-9 also (although :h + -- quote_number seems to state otherwise for cuts to specific registers..?). + saved_registers = store_registers( + "", + "1", + "2", + "3", + "4", + "5", + "6", + "7", + "8", + "9", + "z" + ) + + -- store data needed for de-indenting lines. + start_line = vim.fn.line("'<") - 1 + start_col = vim.fn.col("'<") + end_line = vim.fn.line("'>") - 1 + end_col = vim.fn.col("'>") + -- +1: include final line. + lines = vim.api.nvim_buf_get_lines(0, start_line, end_line + 1, true) + mode = vim.fn.visualmode() +end + +function M.post_cut(register_name) + -- remove trailing newline. + local chunks = vim.split(vim.fn.getreg(register_name):gsub("\n$", ""), "\n") + + -- make sure to restore the registers to the state they were before cutting. + restore_registers(saved_registers) + + local tm_select, select_dedent = vim.deepcopy(chunks), vim.deepcopy(chunks) + + local min_indent = get_min_indent(lines) or "" + if mode == "V" then + tm_select[1] = tm_select[1]:gsub("^%s+", "") + -- remove indent from all lines: + for i = 1, #select_dedent do + select_dedent[i] = select_dedent[i]:gsub("^" .. min_indent, "") + end + -- due to the trailing newline of the last line, and vim.split's + -- behaviour, the last line of `chunks` is always empty. + -- Keep this + elseif mode == "v" then + -- if selection starts inside indent, remove indent. + if #min_indent > start_col then + select_dedent[1] = lines[1]:gsub(min_indent, "") + end + for i = 2, #select_dedent - 1 do + select_dedent[i] = select_dedent[i]:gsub(min_indent, "") + end + + -- remove as much indent from the last line as possible. + if #min_indent > end_col then + select_dedent[#select_dedent] = "" + else + select_dedent[#select_dedent] = + select_dedent[#select_dedent]:gsub("^" .. min_indent, "") + end + else + -- in block: if indent is in block, remove the part of it that is inside + -- it for select_dedent. + if #min_indent > start_col then + local indent_in_block = min_indent:sub(start_col, #min_indent) + for i, line in ipairs(chunks) do + select_dedent[i] = line:gsub("^" .. indent_in_block, "") + end + end + end + + vim.api.nvim_buf_set_var(0, SELECT_RAW, chunks) + vim.api.nvim_buf_set_var(0, SELECT_DEDENT, select_dedent) + vim.api.nvim_buf_set_var(0, TM_SELECT, tm_select) + + lines = nil + start_line, start_col, end_line, end_col = nil, nil, nil, nil + mode = nil +end + +return M diff --git a/lua/luasnip/util/util.lua b/lua/luasnip/util/util.lua index 3018278a8..0d3763289 100644 --- a/lua/luasnip/util/util.lua +++ b/lua/luasnip/util/util.lua @@ -268,152 +268,6 @@ local function wrap_nodes(nodes) end end -local SELECT_RAW = "LUASNIP_SELECT_RAW" -local SELECT_DEDENT = "LUASNIP_SELECT_DEDENT" -local TM_SELECT = "LUASNIP_TM_SELECT" - -local function get_selection() - local ok, val = pcall(vim.api.nvim_buf_get_var, 0, SELECT_RAW) - if ok then - local result = { - val, - vim.api.nvim_buf_get_var(0, SELECT_DEDENT), - vim.api.nvim_buf_get_var(0, TM_SELECT), - } - - vim.api.nvim_buf_del_var(0, SELECT_RAW) - vim.api.nvim_buf_del_var(0, SELECT_DEDENT) - vim.api.nvim_buf_del_var(0, TM_SELECT) - - return unpack(result) - end - return {}, {}, {} -end - -local function get_min_indent(lines) - -- "^(%s*)%S": match only lines that actually contain text. - local min_indent = lines[1]:match("^(%s*)%S") - for i = 2, #lines do - -- %s* -> at least matches - local line_indent = lines[i]:match("^(%s*)%S") - -- ignore if not matched. - if line_indent then - -- if no line until now matched, use line_indent. - if not min_indent or #line_indent < #min_indent then - min_indent = line_indent - end - end - end - return min_indent -end - --- nvim0.7 is missing vim.v.maxcol :/ --- but this should suffice. -local maxcol = vim.v.maxcol or 2147483647 --- displaycol_to may be nil for end-of-line -local function displaycol_trim_line(line, displaycol_from, displaycol_to) - local line_len = vim.str_utfindex(line, #line) - - -- bytes are 0-based, start- and end-inclusive. - -- str_byteindex gives last byte, get first byte is last byte of previous - -- symbol +1. - local start_byte = vim.str_byteindex(line, displaycol_from - 1) + 1 - local end_byte = - vim.str_byteindex(line, math.min(displaycol_to or maxcol, line_len)) - - return line:sub(start_byte, end_byte) -end - -local function store_selection() - -- _col are positions in display-columns, so utf-index, 1-based. - -- lines are 0-based. - local _, start_line, start_col, _ = unpack(vim.fn.getcharpos("'<")) - start_line = start_line - 1 - - local _, end_line, end_col, _ = unpack(vim.fn.getcharpos("'>")) - end_line = end_line - 1 - - local mode = vim.fn.visualmode() - if - not vim.o.selection == "exclusive" - and not (start_line == end_line and start_col == end_col) - then - end_col = end_col - 1 - end - - -- include final line. - local lines = vim.api.nvim_buf_get_lines(0, start_line, end_line + 1, true) - - local chunks = {} - if start_line == end_line then - chunks = { displaycol_trim_line(lines[1], start_col, end_col) } - else - -- displaycolumns! - local first_col = 1 - local last_col = nil - if mode:lower() ~= "v" then -- mode is block - first_col = start_col - last_col = end_col - end - chunks = { displaycol_trim_line(lines[1], start_col, last_col) } - - -- potentially trim lines (Block). - for cl = 2, #lines - 1 do - table.insert( - chunks, - displaycol_trim_line(lines[cl], first_col, last_col) - ) - end - table.insert( - chunks, - displaycol_trim_line(lines[#lines], first_col, end_col) - ) - end - - -- init with raw selection. - local tm_select, select_dedent = vim.deepcopy(chunks), vim.deepcopy(chunks) - -- may be nil if no indent. - local min_indent = get_min_indent(lines) or "" - -- TM_SELECTED_TEXT contains text from new cursor position(for V the first - -- non-whitespace of first line, v and c-v raw) to end of selection. - if mode == "V" then - tm_select[1] = tm_select[1]:gsub("^%s+", "") - -- remove indent from all lines: - for i = 1, #select_dedent do - select_dedent[i] = select_dedent[i]:gsub("^" .. min_indent, "") - end - elseif mode == "v" then - -- if selection starts inside indent, remove indent. - if #min_indent > start_col then - select_dedent[1] = lines[1]:gsub(min_indent, "") - end - for i = 2, #select_dedent - 1 do - select_dedent[i] = select_dedent[i]:gsub(min_indent, "") - end - - -- remove as much indent from the last line as possible. - if #min_indent > end_col then - select_dedent[#select_dedent] = "" - else - select_dedent[#select_dedent] = - select_dedent[#select_dedent]:gsub("^" .. min_indent, "") - end - else - -- in block: if indent is in block, remove the part of it that is inside - -- it for select_dedent. - if #min_indent > start_col then - local indent_in_block = min_indent:sub(start_col, #min_indent) - for i, line in ipairs(chunks) do - select_dedent[i] = line:gsub("^" .. indent_in_block, "") - end - end - end - - vim.api.nvim_buf_set_var(0, SELECT_RAW, chunks) - vim.api.nvim_buf_set_var(0, SELECT_DEDENT, select_dedent) - vim.api.nvim_buf_set_var(0, TM_SELECT, tm_select) -end - local function pos_equal(p1, p2) return p1[1] == p2[1] and p1[2] == p2[2] end @@ -646,8 +500,6 @@ return { put = put, to_string_table = to_string_table, wrap_nodes = wrap_nodes, - store_selection = store_selection, - get_selection = get_selection, pos_equal = pos_equal, dedent = dedent, indent = indent, diff --git a/tests/integration/parser_spec.lua b/tests/integration/parser_spec.lua index afb2da8a8..af7de223a 100644 --- a/tests/integration/parser_spec.lua +++ b/tests/integration/parser_spec.lua @@ -1,5 +1,5 @@ local helpers = require("test.functional.helpers")(after_each) -local exec_lua, feed = helpers.exec_lua, helpers.feed +local exec_lua, feed, exec = helpers.exec_lua, helpers.feed, helpers.exec local ls_helpers = require("helpers") local Screen = require("test.functional.ui.screen") @@ -725,6 +725,8 @@ describe("Parser", function() -- expand snippet with selected multiline-text. feed("iasdfasdfVk") + -- wait a bit.. + exec('call wait(200, "0")') exec_lua("ls.lsp_expand([[" .. snip .. "]])") screen:expect({ diff --git a/tests/integration/select_spec.lua b/tests/integration/select_spec.lua new file mode 100644 index 000000000..a3d19e44a --- /dev/null +++ b/tests/integration/select_spec.lua @@ -0,0 +1,176 @@ +local helpers = require("test.functional.helpers")(after_each) +local exec_lua, feed, exec = helpers.exec_lua, helpers.feed, helpers.exec +local ls_helpers = require("helpers") +local Screen = require("test.functional.ui.screen") + +describe("Selection", function() + local screen + + before_each(function() + helpers.clear() + ls_helpers.session_setup_luasnip() + + screen = Screen.new(50, 7) + screen:attach() + screen:set_default_attr_ids({ + [0] = { bold = true, foreground = Screen.colors.Blue }, + [1] = { bold = true, foreground = Screen.colors.Brown }, + [2] = { bold = true }, + [3] = { background = Screen.colors.LightGray }, + }) + end) + + after_each(function() + screen:detach() + end) + + it("works via config-keybinding, and does not alter registers.", function() + exec_lua("regs_pre = vim.api.nvim_get_context({}).regs") + + feed("iasdf qwerv^") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + .asdf qwer.^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + + assert.is_true(exec_lua("return vim.deep_equal(vim.api.nvim_get_context({}).regs, regs_pre)")) + end) + + it("works via manual keybinding.", function() + exec_lua[[ + vim.keymap.set({"x"}, "p", ls.select_keys, {silent = true}) + ]] + feed("iasdf qwerv^p") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + .asdf qwer.^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + end) + + it("works with linewise-selection.", function() + feed("iasdf qwerasdf qwerasdf qwerVkk") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + .asdf qwer | + asdf qwer | + asdf qwer.^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + end) + + it("works with block-select.", function() + feed("iasdf qwerasdf qwerasdf qwer") + screen:expect{grid=[[ + asdf qwer | + asdf qwer | + asdf qwer^ | + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + feed("^kkllohh") + screen:expect{grid=[[ + as{3:df qw}er | + as{3:df qw}er | + as{3:df q}^wer | + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- VISUAL BLOCK --} |]]} + feed("") + feed("Go") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + aser | + aser | + aser | + .df qw | + df qw | + df qw.^ | + {2:-- INSERT --} |]]} + end) + + it("works with virtualedit.", function() + feed(":set virtualedit=block") + feed("iasdf qwerasdf qwerasdf qwer") + feed("kkllll") + screen:expect{grid=[[ + asdf qwe{3:r }^ | + asdf qwe{3:r } | + asdf qwe{3:r } | + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- VISUAL BLOCK --} |]]} + feed("") + feed("Go") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + asdf qwe | + asdf qwe | + asdf qwe | + .r | + r | + r .^ | + {2:-- INSERT --} |]]} + end) + + it("works for multibyte characters.", function() + feed("i 𝔼f-𝔼abcvbb") + screen:expect{grid=[[ + 𝔼f^-{3:𝔼abc} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- VISUAL --} |]]} + feed("") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + 𝔼f.-𝔼abc.^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + end) + + it("works for combined characters.", function() + feed("i ‖͚asdf‖͚qwervbbh") + screen:expect{grid=[[ + ‖͚asd^f{3:‖͚qwer} | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- VISUAL --} |]]} + feed("") + exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) + screen:expect{grid=[[ + ‖͚asd.f‖͚qwer.^ | + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {0:~ }| + {2:-- INSERT --} |]]} + end) + + it("does not destroy any registers.", function() + end) +end) From dd3244f41c3b5c5f1c3cefa173513b71bb926981 Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Fri, 10 Nov 2023 21:38:27 +0000 Subject: [PATCH 2/3] Auto generate docs --- doc/luasnip.txt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/luasnip.txt b/doc/luasnip.txt index 4f3886368..c03598c53 100644 --- a/doc/luasnip.txt +++ b/doc/luasnip.txt @@ -1,4 +1,4 @@ -*luasnip.txt* For NVIM v0.8.0 Last change: 2023 October 17 +*luasnip.txt* For NVIM v0.8.0 Last change: 2023 November 10 ============================================================================== Table of Contents *luasnip-table-of-contents* @@ -3288,7 +3288,8 @@ These are the settings you can provide to `luasnip.setup()`: `'InsertLeave'`, to react to changes done in Insert mode) should work just fine (alternatively, this can also be mapped using `luasnip-delete-check`). - `store_selection_keys`: Mapping for populating `TM_SELECTED_TEXT` and related - variables (not set by default). + variables (not set by default). If you want to set this mapping yourself, map + `ls.select_keys` (not a function, actually a string/key-combination) as a rhs. - `enable_autosnippets`: Autosnippets are disabled by default to minimize performance penalty if unused. Set to `true` to enable. - `ext_opts`: Additional options passed to extmarks. Can be used to add From 21efce7023b541aaf53f0f103af836c03bae9d3c Mon Sep 17 00:00:00 2001 From: L3MON4D3 Date: Fri, 10 Nov 2023 21:38:30 +0000 Subject: [PATCH 3/3] Format with stylua --- lua/luasnip/util/select.lua | 15 +----- tests/integration/select_spec.lua | 85 ++++++++++++++++++++----------- 2 files changed, 58 insertions(+), 42 deletions(-) diff --git a/lua/luasnip/util/select.lua b/lua/luasnip/util/select.lua index c1011feef..decf8fb51 100644 --- a/lua/luasnip/util/select.lua +++ b/lua/luasnip/util/select.lua @@ -70,19 +70,8 @@ function M.pre_cut() -- store registers so we don't change any of them. -- "" is affected since we perform a cut (s), 1-9 also (although :h -- quote_number seems to state otherwise for cuts to specific registers..?). - saved_registers = store_registers( - "", - "1", - "2", - "3", - "4", - "5", - "6", - "7", - "8", - "9", - "z" - ) + saved_registers = + store_registers("", "1", "2", "3", "4", "5", "6", "7", "8", "9", "z") -- store data needed for de-indenting lines. start_line = vim.fn.line("'<") - 1 diff --git a/tests/integration/select_spec.lua b/tests/integration/select_spec.lua index a3d19e44a..4fa3d1459 100644 --- a/tests/integration/select_spec.lua +++ b/tests/integration/select_spec.lua @@ -29,148 +29,175 @@ describe("Selection", function() feed("iasdf qwerv^") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ .asdf qwer.^ | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) - assert.is_true(exec_lua("return vim.deep_equal(vim.api.nvim_get_context({}).regs, regs_pre)")) + assert.is_true( + exec_lua( + "return vim.deep_equal(vim.api.nvim_get_context({}).regs, regs_pre)" + ) + ) end) it("works via manual keybinding.", function() - exec_lua[[ + exec_lua([[ vim.keymap.set({"x"}, "p", ls.select_keys, {silent = true}) - ]] + ]]) feed("iasdf qwerv^p") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ .asdf qwer.^ | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) it("works with linewise-selection.", function() feed("iasdf qwerasdf qwerasdf qwerVkk") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ .asdf qwer | asdf qwer | asdf qwer.^ | {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) it("works with block-select.", function() feed("iasdf qwerasdf qwerasdf qwer") - screen:expect{grid=[[ + screen:expect({ + grid = [[ asdf qwer | asdf qwer | asdf qwer^ | {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) feed("^kkllohh") - screen:expect{grid=[[ + screen:expect({ + grid = [[ as{3:df qw}er | as{3:df qw}er | as{3:df q}^wer | {0:~ }| {0:~ }| {0:~ }| - {2:-- VISUAL BLOCK --} |]]} + {2:-- VISUAL BLOCK --} |]], + }) feed("") feed("Go") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ aser | aser | aser | .df qw | df qw | df qw.^ | - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) it("works with virtualedit.", function() feed(":set virtualedit=block") feed("iasdf qwerasdf qwerasdf qwer") feed("kkllll") - screen:expect{grid=[[ + screen:expect({ + grid = [[ asdf qwe{3:r }^ | asdf qwe{3:r } | asdf qwe{3:r } | {0:~ }| {0:~ }| {0:~ }| - {2:-- VISUAL BLOCK --} |]]} + {2:-- VISUAL BLOCK --} |]], + }) feed("") feed("Go") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ asdf qwe | asdf qwe | asdf qwe | .r | r | r .^ | - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) it("works for multibyte characters.", function() feed("i 𝔼f-𝔼abcvbb") - screen:expect{grid=[[ + screen:expect({ + grid = [[ 𝔼f^-{3:𝔼abc} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- VISUAL --} |]]} + {2:-- VISUAL --} |]], + }) feed("") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ 𝔼f.-𝔼abc.^ | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) it("works for combined characters.", function() feed("i ‖͚asdf‖͚qwervbbh") - screen:expect{grid=[[ + screen:expect({ + grid = [[ ‖͚asd^f{3:‖͚qwer} | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- VISUAL --} |]]} + {2:-- VISUAL --} |]], + }) feed("") exec_lua([[ls.lsp_expand(".$LS_SELECT_RAW.")]]) - screen:expect{grid=[[ + screen:expect({ + grid = [[ ‖͚asd.f‖͚qwer.^ | {0:~ }| {0:~ }| {0:~ }| {0:~ }| {0:~ }| - {2:-- INSERT --} |]]} + {2:-- INSERT --} |]], + }) end) - it("does not destroy any registers.", function() - end) + it("does not destroy any registers.", function() end) end)