aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cmake.deps/deps.txt4
-rw-r--r--runtime/doc/insert.txt4
-rw-r--r--runtime/doc/lsp.txt6
-rw-r--r--runtime/doc/motion.txt49
-rw-r--r--runtime/lua/vim/_meta/json.lua3
-rw-r--r--runtime/lua/vim/lsp/codelens.lua39
-rw-r--r--runtime/lua/vim/lsp/util.lua60
-rw-r--r--runtime/lua/vim/treesitter/languagetree.lua4
-rw-r--r--src/nvim/CMakeLists.txt5
-rw-r--r--src/nvim/api/extmark.c60
-rw-r--r--src/nvim/api/ui_events.in.h3
-rw-r--r--src/nvim/api/win_config.c18
-rw-r--r--src/nvim/decoration.c18
-rw-r--r--src/nvim/drawline.c48
-rw-r--r--src/nvim/drawscreen.c12
-rw-r--r--src/nvim/event/process.c1
-rw-r--r--src/nvim/ex_docmd.c4
-rw-r--r--src/nvim/fold.c9
-rw-r--r--src/nvim/grid_defs.h2
-rw-r--r--src/nvim/mouse.c94
-rw-r--r--src/nvim/tui/tui.c16
-rw-r--r--src/nvim/ui_client.h3
-rw-r--r--test/functional/api/extmark_spec.lua18
-rw-r--r--test/functional/api/window_spec.lua22
-rw-r--r--test/functional/fixtures/fake-lsp-server.lua22
-rw-r--r--test/functional/plugin/lsp_spec.lua61
-rw-r--r--test/functional/treesitter/highlight_spec.lua64
-rw-r--r--test/functional/ui/cmdline_spec.lua19
-rw-r--r--test/functional/ui/decorations_spec.lua34
-rw-r--r--test/functional/ui/float_spec.lua201
-rw-r--r--test/functional/ui/fold_spec.lua45
-rw-r--r--test/functional/ui/mouse_spec.lua4
-rw-r--r--test/functional/ui/multigrid_spec.lua1
-rw-r--r--test/old/testdir/test_normal.vim33
-rw-r--r--test/old/testdir/test_virtualedit.vim78
35 files changed, 820 insertions, 244 deletions
diff --git a/cmake.deps/deps.txt b/cmake.deps/deps.txt
index 945f8d5f45..4c23803560 100644
--- a/cmake.deps/deps.txt
+++ b/cmake.deps/deps.txt
@@ -4,8 +4,8 @@ LIBUV_SHA256 7aa66be3413ae10605e1f5c9ae934504ffe317ef68ea16fdaa83e23905c681bd
MSGPACK_URL https://github.com/msgpack/msgpack-c/releases/download/c-6.0.0/msgpack-c-6.0.0.tar.gz
MSGPACK_SHA256 3654f5e2c652dc52e0a993e270bb57d5702b262703f03771c152bba51602aeba
-LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/e897c5743f97a6b05c59852709092e7da4119914.tar.gz
-LUAJIT_SHA256 f28030c61602bffd28a445f3fef23198264a845aab94d909587bac6c12c8b874
+LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/d1a2fef8a8f53b0055ee041f7f63d83a27444ffa.tar.gz
+LUAJIT_SHA256 7d4dd85cbf224c70f2014203903202a90f3cfe7b1c6e7b1b36f8ae472dfc7fba
LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz
LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333
diff --git a/runtime/doc/insert.txt b/runtime/doc/insert.txt
index 0fa5ffd598..ce2ec36ca3 100644
--- a/runtime/doc/insert.txt
+++ b/runtime/doc/insert.txt
@@ -1093,11 +1093,11 @@ To get basic "autocompletion" without installing a plugin, try this script: >lua
vim.api.nvim_create_autocmd("InsertCharPre", {
buffer = vim.api.nvim_get_current_buf(),
callback = function()
- if vim.fn.pumvisible() == 1 then
+ if vim.fn.pumvisible() == 1 or vim.fn.state("m") == "m" then
return
end
local char = vim.v.char
- if vim.tbl_contains(triggers, char) then
+ if vim.list_contains(triggers, char) then
local key = vim.keycode("<C-x><C-n>")
vim.api.nvim_feedkeys(key, "m", false)
end
diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt
index 15292cd7cf..23902f8bc3 100644
--- a/runtime/doc/lsp.txt
+++ b/runtime/doc/lsp.txt
@@ -1430,7 +1430,7 @@ display({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.display()*
Display the lenses using virtual text
Parameters: ~
- • {lenses} (table) of lenses to display (`CodeLens[] | null`)
+ • {lenses} lsp.CodeLens[]|nil lenses to display
• {bufnr} (integer)
• {client_id} (integer)
@@ -1442,7 +1442,7 @@ get({bufnr}) *vim.lsp.codelens.get()*
buffer.
Return: ~
- (table) (`CodeLens[]`)
+ lsp.CodeLens[]
*vim.lsp.codelens.on_codelens()*
on_codelens({err}, {result}, {ctx}, {_})
@@ -1464,7 +1464,7 @@ save({lenses}, {bufnr}, {client_id}) *vim.lsp.codelens.save()*
Store lenses for a specific buffer and client
Parameters: ~
- • {lenses} (table) of lenses to store (`CodeLens[] | null`)
+ • {lenses} lsp.CodeLens[]|nil lenses to store
• {bufnr} (integer)
• {client_id} (integer)
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index c4e144ab29..5e18595d22 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -1050,14 +1050,14 @@ can go to cursor positions before older jumps, and back again. Thus you can
move up and down the list. There is a separate jump list for each window.
The maximum number of entries is fixed at 100.
-For example, after three jump commands you have this jump list:
-
- jump line col file/text ~
- 3 1 0 some text ~
- 2 70 0 another line ~
- 1 1154 23 end. ~
- > ~
+For example, after three jump commands you have this jump list: >
+ jump line col file/text
+ 3 1 0 some text
+ 2 70 0 another line
+ 1 1154 23 end.
+ >
+<
The "file/text" column shows the file name, or the text at the jump if it is
in the current file (an indent is removed and a long line is truncated to fit
in the window).
@@ -1066,14 +1066,14 @@ The marker ">" indicates the current position in the jumplist. It may not be
shown when filtering the |:jumps| command using |:filter|
You are currently in line 1167. If you then use the CTRL-O command, the
-cursor is put in line 1154. This results in:
-
- jump line col file/text ~
- 2 1 0 some text ~
- 1 70 0 another line ~
- > 0 1154 23 end. ~
- 1 1167 0 foo bar ~
+cursor is put in line 1154. This results in: >
+ jump line col file/text
+ 2 1 0 some text
+ 1 70 0 another line
+ > 0 1154 23 end.
+ 1 1167 0 foo bar
+<
The pointer will be set at the last used jump position. The next CTRL-O
command will use the entry above it, the next CTRL-I command will use the
entry below it. If the pointer is below the last entry, this indicates that
@@ -1097,15 +1097,15 @@ command. You can explicitly add a jump by setting the ' mark with "m'". Note
that calling setpos() does not do this.
After the CTRL-O command that got you into line 1154 you could give another
-jump command (e.g., "G"). The jump list would then become:
-
- jump line col file/text ~
- 4 1 0 some text ~
- 3 70 0 another line ~
- 2 1167 0 foo bar ~
- 1 1154 23 end. ~
- > ~
-
+jump command (e.g., "G"). The jump list would then become: >
+
+ jump line col file/text
+ 4 1 0 some text
+ 3 70 0 another line
+ 2 1167 0 foo bar
+ 1 1154 23 end.
+ >
+<
The line numbers will be adjusted for deleted and inserted lines. This fails
if you stop editing a file without writing, like with ":n!".
@@ -1141,7 +1141,6 @@ locations being removed: >
1 462 36 eval.c <-- location X
>
<
-
Then, when yet another location Z is jumped to, the new location Y appears
directly after location X in the jumplist and location X remains in the same
position relative to the locations (X-1, X-2, etc., ...) that had been before
@@ -1151,7 +1150,7 @@ it prior to the original jump from X to Y: >
4 1260 8 mark.c <-- location X-2
3 685 0 eval.c <-- location X-1
2 462 36 eval.c <-- location X
- 1 100 0 buffer.c <-- location Y
+ 1 100 0 buffer.c <-- location Y
>
<
CHANGE LIST JUMPS *changelist* *change-list-jumps* *E664*
diff --git a/runtime/lua/vim/_meta/json.lua b/runtime/lua/vim/_meta/json.lua
index edf905c7c6..e010086615 100644
--- a/runtime/lua/vim/_meta/json.lua
+++ b/runtime/lua/vim/_meta/json.lua
@@ -1,5 +1,8 @@
---@meta
+---@nodoc
+vim.json = {}
+
-- luacheck: no unused args
---@defgroup vim.json
diff --git a/runtime/lua/vim/lsp/codelens.lua b/runtime/lua/vim/lsp/codelens.lua
index 384d09ee48..9cccaa1d66 100644
--- a/runtime/lua/vim/lsp/codelens.lua
+++ b/runtime/lua/vim/lsp/codelens.lua
@@ -8,6 +8,7 @@ local M = {}
--- to throttle refreshes to at most one at a time
local active_refreshes = {}
+---@type table<integer, table<integer, lsp.CodeLens[]>>
--- bufnr -> client_id -> lenses
local lens_cache_by_buf = setmetatable({}, {
__index = function(t, b)
@@ -16,6 +17,8 @@ local lens_cache_by_buf = setmetatable({}, {
end,
})
+---@type table<integer, integer>
+---client_id -> namespace
local namespaces = setmetatable({}, {
__index = function(t, key)
local value = api.nvim_create_namespace('vim_lsp_codelens:' .. key)
@@ -27,6 +30,18 @@ local namespaces = setmetatable({}, {
---@private
M.__namespaces = namespaces
+local augroup = api.nvim_create_augroup('vim_lsp_codelens', {})
+
+api.nvim_create_autocmd('LspDetach', {
+ group = augroup,
+ callback = function(ev)
+ M.clear(ev.data.client_id, ev.buf)
+ end,
+})
+
+---@param lens lsp.CodeLens
+---@param bufnr integer
+---@param client_id integer
local function execute_lens(lens, bufnr, client_id)
local line = lens.range.start.line
api.nvim_buf_clear_namespace(bufnr, namespaces[client_id], line, line + 1)
@@ -42,7 +57,7 @@ end
--- Return all lenses for the given buffer
---
---@param bufnr integer Buffer number. 0 can be used for the current buffer.
----@return table (`CodeLens[]`)
+---@return lsp.CodeLens[]
function M.get(bufnr)
local lenses_by_client = lens_cache_by_buf[bufnr or 0]
if not lenses_by_client then
@@ -98,12 +113,17 @@ end
---@param client_id integer|nil filter by client_id. All clients if nil
---@param bufnr integer|nil filter by buffer. All buffers if nil
function M.clear(client_id, bufnr)
- local buffers = bufnr and { resolve_bufnr(bufnr) } or vim.tbl_keys(lens_cache_by_buf)
+ bufnr = bufnr and resolve_bufnr(bufnr)
+ local buffers = bufnr and { bufnr }
+ or vim.tbl_filter(api.nvim_buf_is_loaded, api.nvim_list_bufs())
for _, iter_bufnr in pairs(buffers) do
local client_ids = client_id and { client_id } or vim.tbl_keys(namespaces)
for _, iter_client_id in pairs(client_ids) do
local ns = namespaces[iter_client_id]
- lens_cache_by_buf[iter_bufnr][iter_client_id] = {}
+ -- there can be display()ed lenses, which are not stored in cache
+ if lens_cache_by_buf[iter_bufnr] then
+ lens_cache_by_buf[iter_bufnr][iter_client_id] = {}
+ end
api.nvim_buf_clear_namespace(iter_bufnr, ns, 0, -1)
end
end
@@ -111,7 +131,7 @@ end
--- Display the lenses using virtual text
---
----@param lenses table of lenses to display (`CodeLens[] | null`)
+---@param lenses? lsp.CodeLens[] lenses to display
---@param bufnr integer
---@param client_id integer
function M.display(lenses, bufnr, client_id)
@@ -124,7 +144,8 @@ function M.display(lenses, bufnr, client_id)
api.nvim_buf_clear_namespace(bufnr, ns, 0, -1)
return
end
- local lenses_by_lnum = {}
+
+ local lenses_by_lnum = {} ---@type table<integer, lsp.CodeLens[]>
for _, lens in pairs(lenses) do
local line_lenses = lenses_by_lnum[lens.range.start.line]
if not line_lenses then
@@ -160,7 +181,7 @@ end
--- Store lenses for a specific buffer and client
---
----@param lenses table of lenses to store (`CodeLens[] | null`)
+---@param lenses? lsp.CodeLens[] lenses to store
---@param bufnr integer
---@param client_id integer
function M.save(lenses, bufnr, client_id)
@@ -174,7 +195,7 @@ function M.save(lenses, bufnr, client_id)
lens_cache_by_buf[bufnr] = lenses_by_client
local ns = namespaces[client_id]
api.nvim_buf_attach(bufnr, false, {
- on_detach = function(b)
+ on_detach = function(_, b)
lens_cache_by_buf[b] = nil
end,
on_lines = function(_, b, _, first_lnum, last_lnum)
@@ -185,6 +206,10 @@ function M.save(lenses, bufnr, client_id)
lenses_by_client[client_id] = lenses
end
+---@param lenses? lsp.CodeLens[]
+---@param bufnr integer
+---@param client_id integer
+---@param callback fun()
local function resolve_lenses(lenses, bufnr, client_id, callback)
lenses = lenses or {}
local num_lens = vim.tbl_count(lenses)
diff --git a/runtime/lua/vim/lsp/util.lua b/runtime/lua/vim/lsp/util.lua
index 988057f5f9..0d1e3cc0d1 100644
--- a/runtime/lua/vim/lsp/util.lua
+++ b/runtime/lua/vim/lsp/util.lua
@@ -2230,6 +2230,35 @@ function M.lookup_section(settings, section)
return settings
end
+--- Converts line range (0-based, end-inclusive) to lsp range,
+--- handles absence of a trailing newline
+---
+---@param bufnr integer
+---@param start_line integer
+---@param end_line integer
+---@param offset_encoding lsp.PositionEncodingKind
+---@return lsp.Range
+local function make_line_range_params(bufnr, start_line, end_line, offset_encoding)
+ local last_line = api.nvim_buf_line_count(bufnr) - 1
+
+ ---@type lsp.Position
+ local end_pos
+
+ if end_line == last_line and not vim.api.nvim_get_option_value('endofline', { buf = bufnr }) then
+ end_pos = {
+ line = end_line,
+ character = M.character_offset(bufnr, end_line, #get_line(bufnr, end_line), offset_encoding),
+ }
+ else
+ end_pos = { line = end_line + 1, character = 0 }
+ end
+
+ return {
+ start = { line = start_line, character = 0 },
+ ['end'] = end_pos,
+ }
+end
+
---@private
--- Request updated LSP information for a buffer.
---
@@ -2253,6 +2282,8 @@ function M._refresh(method, opts)
return
end
+ local textDocument = M.make_text_document_params(bufnr)
+
local only_visible = opts.only_visible or false
if only_visible then
@@ -2260,28 +2291,25 @@ function M._refresh(method, opts)
if api.nvim_win_get_buf(window) == bufnr then
local first = vim.fn.line('w0', window)
local last = vim.fn.line('w$', window)
- local params = {
- textDocument = M.make_text_document_params(bufnr),
- range = {
- start = { line = first - 1, character = 0 },
- ['end'] = { line = last, character = 0 },
- },
- }
for _, client in ipairs(clients) do
- client.request(method, params, nil, bufnr)
+ client.request(method, {
+ textDocument = textDocument,
+ range = make_line_range_params(bufnr, first - 1, last - 1, client.offset_encoding),
+ }, nil, bufnr)
end
end
end
else
- local params = {
- textDocument = M.make_text_document_params(bufnr),
- range = {
- start = { line = 0, character = 0 },
- ['end'] = { line = api.nvim_buf_line_count(bufnr), character = 0 },
- },
- }
for _, client in ipairs(clients) do
- client.request(method, params, nil, bufnr)
+ client.request(method, {
+ textDocument = textDocument,
+ range = make_line_range_params(
+ bufnr,
+ 0,
+ api.nvim_buf_line_count(bufnr) - 1,
+ client.offset_encoding
+ ),
+ }, nil, bufnr)
end
end
end
diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua
index b2c4e9167d..670f2797b7 100644
--- a/runtime/lua/vim/treesitter/languagetree.lua
+++ b/runtime/lua/vim/treesitter/languagetree.lua
@@ -650,8 +650,8 @@ function LanguageTree:included_regions()
return self._regions
end
- if not self._has_regions or next(self._trees) == nil then
- -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1}
+ if not self._has_regions then
+ -- treesitter.c will default empty ranges to { -1, -1, -1, -1, -1, -1} (the full range)
return { {} }
end
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 94e5e6ee28..db79de63cc 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -230,6 +230,11 @@ if(WIN32)
endif()
elseif(APPLE)
target_link_libraries(nvim PRIVATE "-framework CoreServices")
+
+ # Actually export symbols - symbols may not be visible even though
+ # ENABLE_EXPORTS is set to true. See
+ # https://github.com/neovim/neovim/issues/25295
+ set_target_properties(nvim PROPERTIES LINK_FLAGS "-Wl,-export_dynamic")
endif()
if(UNIX)
diff --git a/src/nvim/api/extmark.c b/src/nvim/api/extmark.c
index faab6e593c..91e197bea7 100644
--- a/src/nvim/api/extmark.c
+++ b/src/nvim/api/extmark.c
@@ -113,6 +113,36 @@ static Object hl_group_name(int hl_id, bool hl_name)
}
}
+Array virt_text_to_array(VirtText vt, bool hl_name)
+{
+ Array chunks = ARRAY_DICT_INIT;
+ Array hl_array = ARRAY_DICT_INIT;
+ for (size_t i = 0; i < kv_size(vt); i++) {
+ char *text = kv_A(vt, i).text;
+ int hl_id = kv_A(vt, i).hl_id;
+ if (text == NULL) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ continue;
+ }
+ Array chunk = ARRAY_DICT_INIT;
+ ADD(chunk, CSTR_TO_OBJ(text));
+ if (hl_array.size > 0) {
+ if (hl_id > 0) {
+ ADD(hl_array, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunk, ARRAY_OBJ(hl_array));
+ hl_array = (Array)ARRAY_DICT_INIT;
+ } else if (hl_id > 0) {
+ ADD(chunk, hl_group_name(hl_id, hl_name));
+ }
+ ADD(chunks, ARRAY_OBJ(chunk));
+ }
+ assert(hl_array.size == 0);
+ return chunks;
+}
+
static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict, bool hl_name)
{
Array rv = ARRAY_DICT_INIT;
@@ -145,16 +175,7 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
}
if (kv_size(decor->virt_text)) {
- Array chunks = ARRAY_DICT_INIT;
- for (size_t i = 0; i < decor->virt_text.size; i++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &decor->virt_text.items[i];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ Array chunks = virt_text_to_array(decor->virt_text, hl_name);
PUT(dict, "virt_text", ARRAY_OBJ(chunks));
PUT(dict, "virt_text_hide", BOOLEAN_OBJ(decor->virt_text_hide));
if (decor->virt_text_pos == kVTWinCol) {
@@ -171,19 +192,9 @@ static Array extmark_to_array(const ExtmarkInfo *extmark, bool id, bool add_dict
if (kv_size(decor->virt_lines)) {
Array all_chunks = ARRAY_DICT_INIT;
bool virt_lines_leftcol = false;
- for (size_t i = 0; i < decor->virt_lines.size; i++) {
- Array chunks = ARRAY_DICT_INIT;
- VirtText *vt = &decor->virt_lines.items[i].line;
- virt_lines_leftcol = decor->virt_lines.items[i].left_col;
- for (size_t j = 0; j < vt->size; j++) {
- Array chunk = ARRAY_DICT_INIT;
- VirtTextChunk *vtc = &vt->items[j];
- ADD(chunk, CSTR_TO_OBJ(vtc->text));
- if (vtc->hl_id > 0) {
- ADD(chunk, hl_group_name(vtc->hl_id, hl_name));
- }
- ADD(chunks, ARRAY_OBJ(chunk));
- }
+ for (size_t i = 0; i < kv_size(decor->virt_lines); i++) {
+ virt_lines_leftcol = kv_A(decor->virt_lines, i).left_col;
+ Array chunks = virt_text_to_array(kv_A(decor->virt_lines, i).line, hl_name);
ADD(all_chunks, ARRAY_OBJ(chunks));
}
PUT(dict, "virt_lines", ARRAY_OBJ(all_chunks));
@@ -1188,8 +1199,7 @@ VirtText parse_virt_text(Array chunks, Error *err, int *width)
goto free_exit;
}
if (j < arr.size - 1) {
- kv_push(virt_text, ((VirtTextChunk){ .text = NULL,
- .hl_id = hl_id }));
+ kv_push(virt_text, ((VirtTextChunk){ .text = NULL, .hl_id = hl_id }));
}
}
} else {
diff --git a/src/nvim/api/ui_events.in.h b/src/nvim/api/ui_events.in.h
index fc70215352..6ca5024a04 100644
--- a/src/nvim/api/ui_events.in.h
+++ b/src/nvim/api/ui_events.in.h
@@ -167,4 +167,7 @@ void msg_history_show(Array entries)
FUNC_API_SINCE(6) FUNC_API_REMOTE_ONLY;
void msg_history_clear(void)
FUNC_API_SINCE(10) FUNC_API_REMOTE_ONLY;
+
+void error_exit(Integer status)
+ FUNC_API_SINCE(12);
#endif // NVIM_API_UI_EVENTS_IN_H
diff --git a/src/nvim/api/win_config.c b/src/nvim/api/win_config.c
index ebdbd896a5..ea0b7ce512 100644
--- a/src/nvim/api/win_config.c
+++ b/src/nvim/api/win_config.c
@@ -259,34 +259,28 @@ void nvim_win_set_config(Window window, Dict(float_config) *config, Error *err)
Dictionary config_put_bordertext(Dictionary config, FloatConfig *fconfig,
BorderTextType bordertext_type)
{
- VirtText chunks;
+ VirtText vt;
AlignTextPos align;
char *field_name;
char *field_pos_name;
switch (bordertext_type) {
case kBorderTextTitle:
- chunks = fconfig->title_chunks;
+ vt = fconfig->title_chunks;
align = fconfig->title_pos;
field_name = "title";
field_pos_name = "title_pos";
break;
case kBorderTextFooter:
- chunks = fconfig->footer_chunks;
+ vt = fconfig->footer_chunks;
align = fconfig->footer_pos;
field_name = "footer";
field_pos_name = "footer_pos";
break;
+ default:
+ abort();
}
- Array bordertext = ARRAY_DICT_INIT;
- for (size_t i = 0; i < chunks.size; i++) {
- Array tuple = ARRAY_DICT_INIT;
- ADD(tuple, CSTR_TO_OBJ(chunks.items[i].text));
- if (chunks.items[i].hl_id > 0) {
- ADD(tuple, CSTR_TO_OBJ(syn_id2name(chunks.items[i].hl_id)));
- }
- ADD(bordertext, ARRAY_OBJ(tuple));
- }
+ Array bordertext = virt_text_to_array(vt, true);
PUT(config, field_name, ARRAY_OBJ(bordertext));
char *pos;
diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c
index b70f070a51..9d391cde8c 100644
--- a/src/nvim/decoration.c
+++ b/src/nvim/decoration.c
@@ -153,6 +153,24 @@ void clear_virttext(VirtText *text)
*text = (VirtText)KV_INITIAL_VALUE;
}
+/// Get the next chunk of a virtual text item.
+///
+/// @param[in] vt The virtual text item
+/// @param[in,out] pos Position in the virtual text item
+/// @param[in,out] attr Highlight attribute
+///
+/// @return The text of the chunk, or NULL if there are no more chunks
+char *next_virt_text_chunk(VirtText vt, size_t *pos, int *attr)
+{
+ char *text = NULL;
+ for (; text == NULL && *pos < kv_size(vt); (*pos)++) {
+ text = kv_A(vt, *pos).text;
+ int hl_id = kv_A(vt, *pos).hl_id;
+ *attr = hl_combine_attr(*attr, hl_id > 0 ? syn_id2attr(hl_id) : 0);
+ }
+ return text;
+}
+
Decoration *decor_find_virttext(buf_T *buf, int row, uint64_t ns_id)
{
MarkTreeIter itr[1] = { 0 };
diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c
index 73021d83a8..7d64d9fa3c 100644
--- a/src/nvim/drawline.c
+++ b/src/nvim/drawline.c
@@ -332,23 +332,17 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode,
size_t virt_pos = 0;
while (rl ? col > max_col : col < max_col) {
- if (!*s.p) {
+ if (*s.p == NUL) {
if (virt_pos >= kv_size(vt)) {
break;
}
virt_attr = 0;
- do {
- s.p = kv_A(vt, virt_pos).text;
- int hl_id = kv_A(vt, virt_pos).hl_id;
- virt_attr = hl_combine_attr(virt_attr,
- hl_id > 0 ? syn_id2attr(hl_id) : 0);
- virt_pos++;
- } while (!s.p && virt_pos < kv_size(vt));
- if (!s.p) {
+ s.p = next_virt_text_chunk(vt, &virt_pos, &virt_attr);
+ if (s.p == NULL) {
break;
}
}
- if (!*s.p) {
+ if (*s.p == NUL) {
continue;
}
int attr;
@@ -950,17 +944,20 @@ static void handle_inline_virtual_text(win_T *wp, winlinevars_T *wlv, ptrdiff_t
}
} else {
// already inside existing inline virtual text with multiple chunks
- VirtTextChunk vtc = kv_A(wlv->virt_inline, wlv->virt_inline_i);
- wlv->virt_inline_i++;
- wlv->p_extra = vtc.text;
- wlv->n_extra = (int)strlen(vtc.text);
+ int attr = 0;
+ char *text = next_virt_text_chunk(wlv->virt_inline, &wlv->virt_inline_i, &attr);
+ if (text == NULL) {
+ continue;
+ }
+ wlv->p_extra = text;
+ wlv->n_extra = (int)strlen(text);
if (wlv->n_extra == 0) {
continue;
}
wlv->c_extra = NUL;
wlv->c_final = NUL;
- wlv->extra_attr = vtc.hl_id ? syn_id2attr(vtc.hl_id) : 0;
- wlv->n_attr = mb_charlen(vtc.text);
+ wlv->extra_attr = attr;
+ wlv->n_attr = mb_charlen(text);
// If the text didn't reach until the first window
// column we need to skip cells.
if (wlv->skip_cells > 0) {
@@ -3024,14 +3021,17 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
linebuf_attr[wlv.off] = wlv.char_attr;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
-
- if (wlv.draw_state == WL_FOLD) {
- linebuf_vcol[wlv.off] = -2;
+ if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
+ linebuf_vcol[wlv.off] = wlv.vcol;
+ } else if (wlv.draw_state == WL_FOLD) {
if (wlv.n_closing > 0) {
linebuf_vcol[wlv.off] = -3;
wlv.n_closing--;
+ } else {
+ linebuf_vcol[wlv.off] = -2;
}
+ } else {
+ linebuf_vcol[wlv.off] = -1;
}
if (utf_char2cells(mb_c) > 1) {
@@ -3041,17 +3041,19 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl
// UTF-8: Put a 0 in the second screen char.
linebuf_char[wlv.off] = 0;
linebuf_attr[wlv.off] = linebuf_attr[wlv.off - 1];
+
if (wlv.draw_state > WL_STC && wlv.filler_todo <= 0) {
- wlv.vcol++;
+ linebuf_vcol[wlv.off] = ++wlv.vcol;
+ } else {
+ linebuf_vcol[wlv.off] = -1;
}
+
// When "wlv.tocol" is halfway through a character, set it to the end
// of the character, otherwise highlighting won't stop.
if (wlv.tocol == wlv.vcol) {
wlv.tocol++;
}
- linebuf_vcol[wlv.off] = wlv.vcol;
-
if (wp->w_p_rl) {
// now it's time to backup one cell
wlv.off--;
diff --git a/src/nvim/drawscreen.c b/src/nvim/drawscreen.c
index 854c8590e4..1d22b67d40 100644
--- a/src/nvim/drawscreen.c
+++ b/src/nvim/drawscreen.c
@@ -709,13 +709,15 @@ void end_search_hl(void)
screen_search_hl.rm.regprog = NULL;
}
-static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText text_chunks, int row, int col)
+static void win_redr_bordertext(win_T *wp, ScreenGrid *grid, VirtText vt, int row, int col)
{
- for (size_t i = 0; i < text_chunks.size; i++) {
- char *text = text_chunks.items[i].text;
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *text = next_virt_text_chunk(vt, &i, &attr);
+ if (text == NULL) {
+ break;
+ }
int cell = (int)mb_string2cells(text);
- int hl_id = text_chunks.items[i].hl_id;
- int attr = hl_id ? syn_id2attr(hl_id) : 0;
grid_puts(grid, text, row, col, attr);
col += cell;
}
diff --git a/src/nvim/event/process.c b/src/nvim/event/process.c
index 95bf4d1c3b..00ba1334b0 100644
--- a/src/nvim/event/process.c
+++ b/src/nvim/event/process.c
@@ -423,6 +423,7 @@ static void exit_event(void **argv)
if (!exiting) {
if (ui_client_channel_id) {
+ ui_client_exit_status = status;
os_exit(status);
} else {
assert(status == 0); // Called from rpc_close(), which passes 0 as status.
diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c
index 44610c81d8..0a0f7c244d 100644
--- a/src/nvim/ex_docmd.c
+++ b/src/nvim/ex_docmd.c
@@ -4591,7 +4591,9 @@ static void ex_cquit(exarg_T *eap)
FUNC_ATTR_NORETURN
{
// this does not always pass on the exit code to the Manx compiler. why?
- getout(eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE);
+ int status = eap->addr_count > 0 ? (int)eap->line2 : EXIT_FAILURE;
+ ui_call_error_exit(status);
+ getout(status);
}
/// Do preparations for "qall" and "wqall".
diff --git a/src/nvim/fold.c b/src/nvim/fold.c
index 1d5ba49301..d874e904d0 100644
--- a/src/nvim/fold.c
+++ b/src/nvim/fold.c
@@ -3344,8 +3344,13 @@ void f_foldtextresult(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
}
if (kv_size(vt) > 0) {
assert(*text == NUL);
- for (size_t i = 0; i < kv_size(vt); i++) {
- char *new_text = concat_str(text, kv_A(vt, i).text);
+ for (size_t i = 0; i < kv_size(vt);) {
+ int attr = 0;
+ char *new_text = next_virt_text_chunk(vt, &i, &attr);
+ if (new_text == NULL) {
+ break;
+ }
+ new_text = concat_str(text, new_text);
xfree(text);
text = new_text;
}
diff --git a/src/nvim/grid_defs.h b/src/nvim/grid_defs.h
index 207bf72816..100d648263 100644
--- a/src/nvim/grid_defs.h
+++ b/src/nvim/grid_defs.h
@@ -44,7 +44,7 @@ enum {
/// attrs[] contains the highlighting attribute for each cell.
///
/// vcols[] contains the virtual columns in the line. -1 means not available
-/// (below last line), MAXCOL means after the end of the line.
+/// or before buffer text, MAXCOL means after the end of the line.
/// -2 or -3 means in fold column and a mouse click should:
/// -2: open a fold
/// -3: close a fold
diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c
index e35385dd43..9b09a3fdf3 100644
--- a/src/nvim/mouse.c
+++ b/src/nvim/mouse.c
@@ -1849,57 +1849,61 @@ static void mouse_check_grid(colnr_T *vcolp, int *flagsp)
int click_grid = mouse_grid;
int click_row = mouse_row;
int click_col = mouse_col;
- int max_row = Rows;
- int max_col = Columns;
- bool multigrid = ui_has(kUIMultigrid);
- colnr_T col_from_screen = -1;
+ // XXX: this doesn't change click_grid if it is 1, even with multigrid
win_T *wp = mouse_find_win(&click_grid, &click_row, &click_col);
- if (wp && multigrid) {
- max_row = wp->w_grid_alloc.rows;
- max_col = wp->w_grid_alloc.cols;
- }
-
- if (wp && mouse_row >= 0 && mouse_row < max_row
- && mouse_col >= 0 && mouse_col < max_col) {
- ScreenGrid *gp = multigrid ? &wp->w_grid_alloc : &default_grid;
- int use_row = multigrid && mouse_grid == 0 ? click_row : mouse_row;
- int use_col = multigrid && mouse_grid == 0 ? click_col : mouse_col;
-
- if (gp->chars != NULL) {
- const size_t off = gp->line_offset[use_row] + (size_t)use_col;
-
- // Only use vcols[] after the window was redrawn. Mainly matters
- // for tests, a user would not click before redrawing.
- if (wp->w_redr_type == 0) {
- col_from_screen = gp->vcols[off];
- }
-
- if (col_from_screen == MAXCOL) {
- // When clicking after end of line, still need to set correct curswant
- size_t off_l = gp->line_offset[use_row];
- if (gp->vcols[off_l] < MAXCOL) {
- // Binary search to find last char in line
- size_t off_r = off;
- while (off_l < off_r) {
- size_t off_m = (off_l + off_r + 1) / 2;
- if (gp->vcols[off_m] < MAXCOL) {
- off_l = off_m;
- } else {
- off_r = off_m - 1;
- }
- }
- *vcolp = gp->vcols[off_r] + (int)(off - off_r);
+ // Only use vcols[] after the window was redrawn. Mainly matters
+ // for tests, a user would not click before redrawing.
+ if (wp == NULL || wp->w_redr_type != 0) {
+ return;
+ }
+ ScreenGrid *gp = &wp->w_grid;
+ int start_row = 0;
+ int start_col = 0;
+ grid_adjust(&gp, &start_row, &start_col);
+ if (gp->handle != click_grid || gp->chars == NULL) {
+ return;
+ }
+ click_row += start_row;
+ click_col += start_col;
+ if (click_row < 0 || click_row >= gp->rows
+ || click_col < 0 || click_col >= gp->cols) {
+ return;
+ }
+
+ const size_t off = gp->line_offset[click_row] + (size_t)click_col;
+ colnr_T col_from_screen = gp->vcols[off];
+
+ if (col_from_screen == MAXCOL) {
+ // When clicking after end of line, still need to set correct curswant
+ size_t off_l = gp->line_offset[click_row] + (size_t)start_col;
+ if (gp->vcols[off_l] < MAXCOL) {
+ // Binary search to find last char in line
+ size_t off_r = off;
+ while (off_l < off_r) {
+ size_t off_m = (off_l + off_r + 1) / 2;
+ if (gp->vcols[off_m] < MAXCOL) {
+ off_l = off_m;
} else {
- // Shouldn't normally happen
- *vcolp = MAXCOL;
+ off_r = off_m - 1;
}
- } else if (col_from_screen >= 0) {
- // Use the virtual column from vcols[], it is accurate also after
- // concealed characters.
- *vcolp = col_from_screen;
}
+ colnr_T eol_vcol = gp->vcols[off_r];
+ assert(eol_vcol < MAXCOL);
+ if (eol_vcol < 0) {
+ // Empty line or whole line before w_leftcol,
+ // with columns before buffer text
+ eol_vcol = wp->w_leftcol - 1;
+ }
+ *vcolp = eol_vcol + (int)(off - off_r);
+ } else {
+ // Empty line or whole line before w_leftcol
+ *vcolp = click_col - start_col + wp->w_leftcol;
}
+ } else if (col_from_screen >= 0) {
+ // Use the virtual column from vcols[], it is accurate also after
+ // concealed characters.
+ *vcolp = col_from_screen;
}
if (col_from_screen == -2) {
diff --git a/src/nvim/tui/tui.c b/src/nvim/tui/tui.c
index 9fea6442a9..4097b770c9 100644
--- a/src/nvim/tui/tui.c
+++ b/src/nvim/tui/tui.c
@@ -145,6 +145,7 @@ struct TUIData {
} unibi_ext;
char *space_buf;
bool stopped;
+ int seen_error_exit;
int width;
int height;
bool rgb;
@@ -162,6 +163,7 @@ void tui_start(TUIData **tui_p, int *width, int *height, char **term)
tui->is_starting = true;
tui->screenshot = NULL;
tui->stopped = false;
+ tui->seen_error_exit = 0;
tui->loop = &main_loop;
kv_init(tui->invalid_regions);
signal_watcher_init(tui->loop, &tui->winch_handle, tui);
@@ -384,8 +386,13 @@ static void terminfo_stop(TUIData *tui)
unibi_out_ext(tui, tui->unibi_ext.disable_extended_keys);
// May restore old title before exiting alternate screen.
tui_set_title(tui, (String)STRING_INIT);
- // Exit alternate screen.
- unibi_out(tui, unibi_exit_ca_mode);
+ // if nvim exited with nonzero status, without indicated this was an
+ // intentional exit (like `:1cquit`), it likely was an internal failure.
+ // Don't clobber the stderr error message in this case.
+ if (ui_client_exit_status == tui->seen_error_exit) {
+ // Exit alternate screen.
+ unibi_out(tui, unibi_exit_ca_mode);
+ }
if (tui->cursor_color_changed) {
unibi_out_ext(tui, tui->unibi_ext.reset_cursor_color);
}
@@ -443,6 +450,11 @@ static void tui_terminal_stop(TUIData *tui)
terminfo_stop(tui);
}
+void tui_error_exit(TUIData *tui, Integer status)
+{
+ tui->seen_error_exit = (int)status;
+}
+
void tui_stop(TUIData *tui)
{
tui_terminal_stop(tui);
diff --git a/src/nvim/ui_client.h b/src/nvim/ui_client.h
index 7e5f847039..05964422f3 100644
--- a/src/nvim/ui_client.h
+++ b/src/nvim/ui_client.h
@@ -23,6 +23,9 @@ EXTERN sattr_T *grid_line_buf_attr INIT(= NULL);
// ID of the ui client channel. If zero, the client is not running.
EXTERN uint64_t ui_client_channel_id INIT(= 0);
+// exit status from embedded nvim process
+EXTERN int ui_client_exit_status INIT(= 0);
+
// TODO(bfredl): the current structure for how tui and ui_client.c communicate is a bit awkward.
// This will be restructured as part of The UI Devirtualization Project.
diff --git a/test/functional/api/extmark_spec.lua b/test/functional/api/extmark_spec.lua
index c4449bc201..133ec67942 100644
--- a/test/functional/api/extmark_spec.lua
+++ b/test/functional/api/extmark_spec.lua
@@ -1524,16 +1524,19 @@ describe('API/extmarks', function()
sign_hl_group = "Statement",
sign_text = ">>",
spell = true,
- virt_lines = { { { "lines", "Statement" } }},
+ virt_lines = {
+ { { "lines", "Macro" }, { "???" } },
+ { { "stack", { "Type", "Search" } }, { "!!!" } },
+ },
virt_lines_above = true,
virt_lines_leftcol = true,
- virt_text = { { "text", "Statement" } },
+ virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } },
virt_text_hide = true,
virt_text_pos = "right_align",
})
set_extmark(ns, marks[2], 0, 0, {
priority = 0,
- virt_text = { { "text", "Statement" } },
+ virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } },
virt_text_win_col = 1,
})
eq({0, 0, {
@@ -1553,10 +1556,13 @@ describe('API/extmarks', function()
sign_hl_group = "Statement",
sign_text = ">>",
spell = true,
- virt_lines = { { { "lines", "Statement" } }},
+ virt_lines = {
+ { { "lines", "Macro" }, { "???" } },
+ { { "stack", { "Type", "Search" } }, { "!!!" } },
+ },
virt_lines_above = true,
virt_lines_leftcol = true,
- virt_text = { { "text", "Statement" } },
+ virt_text = { { "text", "Macro" }, { "???" }, { "stack", { "Type", "Search" } } },
virt_text_hide = true,
virt_text_pos = "right_align",
} }, get_extmark_by_id(ns, marks[1], { details = true }))
@@ -1564,7 +1570,7 @@ describe('API/extmarks', function()
ns_id = 1,
right_gravity = true,
priority = 0,
- virt_text = { { "text", "Statement" } },
+ virt_text = { { "", "Macro" }, { "", { "Type", "Search" } }, { "" } },
virt_text_hide = false,
virt_text_pos = "win_col",
virt_text_win_col = 1,
diff --git a/test/functional/api/window_spec.lua b/test/functional/api/window_spec.lua
index 44d4470337..6737c2d15b 100644
--- a/test/functional/api/window_spec.lua
+++ b/test/functional/api/window_spec.lua
@@ -895,13 +895,14 @@ describe('API/win', function()
it('includes border', function()
local b = { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h' }
local win = meths.open_win(0, true, {
- relative='win', row=3, col=3, width=12, height=3,
- border = b,
+ relative='win', row=3, col=3, width=12, height=3,
+ border = b,
})
local cfg = meths.win_get_config(win)
eq(b, cfg.border)
end)
+
it('includes border with highlight group', function()
local b = {
{'a', 'Normal'},
@@ -914,12 +915,25 @@ describe('API/win', function()
{'h', 'PreProc'},
}
local win = meths.open_win(0, true, {
- relative='win', row=3, col=3, width=12, height=3,
- border = b,
+ relative='win', row=3, col=3, width=12, height=3,
+ border = b,
})
local cfg = meths.win_get_config(win)
eq(b, cfg.border)
end)
+
+ it('includes title and footer', function()
+ local title = { {'A', {'StatusLine', 'TabLine'}}, {'B'}, {'C', 'WinBar'} }
+ local footer = { {'A', 'WinBar'}, {'B'}, {'C', {'StatusLine', 'TabLine'}} }
+ local win = meths.open_win(0, true, {
+ relative='win', row=3, col=3, width=12, height=3,
+ border = 'single', title = title, footer = footer,
+ })
+
+ local cfg = meths.win_get_config(win)
+ eq(title, cfg.title)
+ eq(footer, cfg.footer)
+ end)
end)
end)
diff --git a/test/functional/fixtures/fake-lsp-server.lua b/test/functional/fixtures/fake-lsp-server.lua
index ef87f6c21a..0db9265a29 100644
--- a/test/functional/fixtures/fake-lsp-server.lua
+++ b/test/functional/fixtures/fake-lsp-server.lua
@@ -949,6 +949,28 @@ function tests.set_defaults_all_capabilities()
}
end
+function tests.inlay_hint()
+ skeleton {
+ on_init = function(params)
+ local expected_capabilities = protocol.make_client_capabilities()
+ assert_eq(params.capabilities, expected_capabilities)
+ return {
+ capabilities = {
+ inlayHintProvider = true;
+ }
+ }
+ end;
+ body = function()
+ notify('start')
+ expect_request('textDocument/inlayHint', function()
+ return nil, {}
+ end)
+ expect_notification("finish")
+ notify('finish')
+ end;
+ }
+end
+
-- Tests will be indexed by test_name
local test_name = arg[1]
local timeout = arg[2]
diff --git a/test/functional/plugin/lsp_spec.lua b/test/functional/plugin/lsp_spec.lua
index 7e30af5058..155c9ad96c 100644
--- a/test/functional/plugin/lsp_spec.lua
+++ b/test/functional/plugin/lsp_spec.lua
@@ -1244,6 +1244,67 @@ describe('LSP', function()
}
end)
+ it('should send correct range for inlay hints with noeol', function()
+ local expected_handlers = {
+ {NIL, {}, {method="shutdown", client_id=1}};
+ {NIL, {}, {method="finish", client_id=1}};
+ {NIL, {}, {
+ method="textDocument/inlayHint",
+ params = {
+ textDocument = {
+ uri = 'file://',
+ },
+ range = {
+ start = { line = 0, character = 0 },
+ ['end'] = { line = 1, character = 3 },
+ }
+ },
+ bufnr=2,
+ client_id=1,
+ }};
+ {NIL, {}, {method="start", client_id=1}};
+ }
+ local client
+ test_rpc_server {
+ test_name = "inlay_hint";
+ on_setup = function()
+ exec_lua [[
+ BUFFER = vim.api.nvim_create_buf(false, true)
+ vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
+ "testing";
+ "123";
+ })
+ vim.bo[BUFFER].eol = false
+ ]]
+ end;
+ on_init = function(_client)
+ client = _client
+ eq(true, client.supports_method('textDocument/inlayHint'))
+ exec_lua [[
+ assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
+ ]]
+ end;
+ on_exit = function(code, signal)
+ eq(0, code, "exit code")
+ eq(0, signal, "exit signal")
+ end;
+ on_handler = function(err, result, ctx)
+ if ctx.method == 'start' then
+ exec_lua [[
+ vim.lsp.inlay_hint(BUFFER, true)
+ ]]
+ end
+ if ctx.method == 'textDocument/inlayHint' then
+ client.notify('finish')
+ end
+ eq(table.remove(expected_handlers), {err, result, ctx}, "expected handler")
+ if ctx.method == 'finish' then
+ client.stop()
+ end
+ end;
+ }
+ end)
+
it('should check the body and didChange incremental', function()
local expected_handlers = {
{NIL, {}, {method="shutdown", client_id=1}};
diff --git a/test/functional/treesitter/highlight_spec.lua b/test/functional/treesitter/highlight_spec.lua
index 0aa0cdd6d6..e037c9e215 100644
--- a/test/functional/treesitter/highlight_spec.lua
+++ b/test/functional/treesitter/highlight_spec.lua
@@ -838,3 +838,67 @@ describe('treesitter highlighting (help)', function()
end)
end)
+
+describe('treesitter highlighting (nested injections)', function()
+ local screen
+
+ before_each(function()
+ screen = Screen.new(80, 7)
+ screen:attach()
+ screen:set_default_attr_ids {
+ [1] = {foreground = Screen.colors.SlateBlue};
+ [2] = {bold = true, foreground = Screen.colors.Brown};
+ [3] = {foreground = Screen.colors.Cyan4};
+ [4] = {foreground = Screen.colors.Fuchsia};
+ }
+ end)
+
+ it("correctly redraws nested injections (GitHub #25252)", function()
+ insert[=[
+function foo() print("Lua!") end
+
+local lorem = {
+ ipsum = {},
+ bar = {},
+}
+vim.cmd([[
+ augroup RustLSP
+ autocmd CursorHold silent! lua vim.lsp.buf.document_highlight()
+ augroup END
+]])
+ ]=]
+
+ exec_lua [[
+ vim.opt.scrolloff = 0
+ vim.bo.filetype = 'lua'
+ vim.treesitter.start()
+ ]]
+
+ -- invalidate the language tree
+ feed("ggi--[[<ESC>04x")
+
+ screen:expect{grid=[[
+ {2:^function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
+ |
+ {2:local} {3:lorem} {2:=} {1:{} |
+ {3:ipsum} {2:=} {1:{},} |
+ {3:bar} {2:=} {1:{},} |
+ {1:}} |
+ |
+ ]]}
+
+ -- spam newline insert/delete to invalidate Lua > Vim > Lua region
+ feed("3jo<ESC>ddko<ESC>ddko<ESC>ddko<ESC>ddk0")
+
+ screen:expect{grid=[[
+ {2:function} {3:foo}{1:()} {1:print(}{4:"Lua!"}{1:)} {2:end} |
+ |
+ {2:local} {3:lorem} {2:=} {1:{} |
+ ^ {3:ipsum} {2:=} {1:{},} |
+ {3:bar} {2:=} {1:{},} |
+ {1:}} |
+ |
+ ]]}
+ end)
+
+end)
diff --git a/test/functional/ui/cmdline_spec.lua b/test/functional/ui/cmdline_spec.lua
index 4b92ab730d..6baad28dad 100644
--- a/test/functional/ui/cmdline_spec.lua
+++ b/test/functional/ui/cmdline_spec.lua
@@ -1275,6 +1275,7 @@ describe('cmdheight=0', function()
it('with multigrid', function()
clear{args={'--cmd', 'set cmdheight=0'}}
screen:attach{ext_multigrid=true}
+ meths.buf_set_lines(0, 0, -1, true, {'p'})
screen:expect{grid=[[
## grid 1
[2:-------------------------]|
@@ -1283,7 +1284,7 @@ describe('cmdheight=0', function()
[2:-------------------------]|
[2:-------------------------]|
## grid 2
- ^ |
+ ^p |
{1:~ }|
{1:~ }|
{1:~ }|
@@ -1302,7 +1303,7 @@ describe('cmdheight=0', function()
[2:-------------------------]|
[3:-------------------------]|
## grid 2
- |
+ {6:p} |
{1:~ }|
{1:~ }|
{1:~ }|
@@ -1427,7 +1428,21 @@ describe('cmdheight=0', function()
|
]])
command('set cmdheight=0')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ ]]}
command('resize -1')
+ screen:expect{grid=[[
+ ^ |
+ {1:~ }|
+ {1:~ }|
+ {2:[No Name] }|
+ |
+ ]]}
command('resize +1')
screen:expect([[
^ |
diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua
index d6a6243be2..f378c50792 100644
--- a/test/functional/ui/decorations_spec.lua
+++ b/test/functional/ui/decorations_spec.lua
@@ -1464,6 +1464,8 @@ describe('extmark decorations', function()
end)
it('can have virtual text which combines foreground and background groups', function()
+ screen:try_resize(20, 5)
+
screen:set_default_attr_ids {
[1] = {bold=true, foreground=Screen.colors.Blue};
[2] = {background = tonumber('0x123456'), foreground = tonumber('0xbbbbbb')};
@@ -1481,30 +1483,24 @@ describe('extmark decorations', function()
hi VeryBold gui=bold
]]
- meths.buf_set_extmark(0, ns, 0, 0, { virt_text={
+ insert('##')
+ local vt = {
{'a', {'BgOne', 'FgEin'}};
{'b', {'BgOne', 'FgZwei'}};
{'c', {'BgTwo', 'FgEin'}};
{'d', {'BgTwo', 'FgZwei'}};
{'X', {'BgTwo', 'FgZwei', 'VeryBold'}};
- }})
-
- screen:expect{grid=[[
- ^ {2:a}{3:b}{4:c}{5:d}{6:X} |
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- {1:~ }|
- |
+ }
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'eol' })
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'right_align' })
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_text = vt, virt_text_pos = 'inline' })
+ meths.buf_set_extmark(0, ns, 0, 0, { virt_lines = { vt, vt } })
+ screen:expect{grid=[[
+ {2:a}{3:b}{4:c}{5:d}{6:X}#^# {2:a}{3:b}{4:c}{5:d}{6:X} {2:a}{3:b}{4:c}{5:d}{6:X}|
+ {2:a}{3:b}{4:c}{5:d}{6:X} |
+ {2:a}{3:b}{4:c}{5:d}{6:X} |
+ {1:~ }|
+ |
]]}
end)
diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua
index f75fb52108..93ca1aef3f 100644
--- a/test/functional/ui/float_spec.lua
+++ b/test/functional/ui/float_spec.lua
@@ -821,7 +821,7 @@ describe('float window', function()
[4] = {bold = true, reverse = true},
[5] = {reverse = true},
[6] = {background = Screen.colors.LightMagenta, bold = true, reverse = true},
- [7] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [7] = {foreground = Screen.colors.White, background = Screen.colors.Red},
[8] = {bold = true, foreground = Screen.colors.SeaGreen4},
[9] = {background = Screen.colors.LightGrey, underline = true},
[10] = {background = Screen.colors.LightGrey, underline = true, bold = true, foreground = Screen.colors.Magenta},
@@ -2512,7 +2512,12 @@ describe('float window', function()
]]}
end
- meths.win_set_config(win, {title= { {"🦄"},{"BB"}},title_pos="right",footer= { {"🦄"},{"BB"}},footer_pos="right"})
+ command('hi B0 guibg=Red guifg=Black')
+ command('hi B1 guifg=White')
+ meths.win_set_config(win, {
+ title = {{"🦄"}, {"BB", {"B0", "B1"}}}, title_pos = "right",
+ footer= {{"🦄"}, {"BB", {"B0", "B1"}}}, footer_pos = "right",
+ })
if multigrid then
screen:expect{grid=[[
## grid 1
@@ -2533,10 +2538,10 @@ describe('float window', function()
## grid 3
|
## grid 4
- {5:╔═════}🦄BB{5:╗}|
+ {5:╔═════}🦄{7:BB}{5:╗}|
{5:║}{1: halloj! }{5:║}|
{5:║}{1: BORDAA }{5:║}|
- {5:╚═════}🦄BB{5:╝}|
+ {5:╚═════}🦄{7:BB}{5:╝}|
]], float_pos={
[4] = { { id = 1001 }, "NW", 1, 2, 5, true }
}, win_viewport={
@@ -2547,10 +2552,10 @@ describe('float window', function()
screen:expect{grid=[[
^ |
{0:~ }|
- {0:~ }{5:╔═════}🦄BB{5:╗}{0: }|
+ {0:~ }{5:╔═════}🦄{7:BB}{5:╗}{0: }|
{0:~ }{5:║}{1: halloj! }{5:║}{0: }|
{0:~ }{5:║}{1: BORDAA }{5:║}{0: }|
- {0:~ }{5:╚═════}🦄BB{5:╝}{0: }|
+ {0:~ }{5:╚═════}🦄{7:BB}{5:╝}{0: }|
|
]]}
end
@@ -6287,7 +6292,7 @@ describe('float window', function()
end
if multigrid then
- meths.input_mouse('left', 'press', '', 1, 0, 0)
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
@@ -6366,7 +6371,7 @@ describe('float window', function()
end
if multigrid then
- meths.input_mouse('left', 'press', '', 1, 0, 0)
+ meths.input_mouse('left', 'press', '', 2, 0, 0)
screen:expect{grid=[[
## grid 1
[2:----------------------------------------]|
@@ -8764,6 +8769,186 @@ describe('float window', function()
end
end)
+ it('left click sets correct curswant in float window with border', function()
+ local buf = meths.create_buf(false,false)
+ meths.buf_set_lines(buf, 0, -1, true, {'', '', ''})
+ meths.open_win(buf, false, {relative='editor', width=20, height=3, row=0, col=5, border='single'})
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ ^ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {5:┌────────────────────┐}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:│}{1: }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [4] = {{id = 1001}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = {id = 1001}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3, sum_scroll_delta = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ ^ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{1: }{5:│}{0: }|
+ {0:~ }{5:│}{1: }{5:│}{0: }|
+ {0:~ }{5:│}{1: }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ |
+ ]]}
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 3, 1)
+ else
+ meths.input_mouse('left', 'press', '', 0, 3, 6)
+ end
+ eq({0, 3, 1, 0, 1}, funcs.getcurpos())
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 3, 2)
+ else
+ meths.input_mouse('left', 'press', '', 0, 3, 7)
+ end
+ eq({0, 3, 1, 0, 2}, funcs.getcurpos())
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 3, 10)
+ else
+ meths.input_mouse('left', 'press', '', 0, 3, 15)
+ end
+ eq({0, 3, 1, 0, 10}, funcs.getcurpos())
+
+ command('setlocal foldcolumn=1')
+ feed('zfkgg')
+ if multigrid then
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {5:┌────────────────────┐}|
+ {5:│}{19: }{1:^ }{5:│}|
+ {5:│}{19:+}{28:+-- 2 lines: ·····}{5:│}|
+ {5:│}{2:~ }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [4] = {{id = 1001}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = {id = 1001}, topline = 0, botline = 4, curline = 0, curcol = 0, linecount = 3, sum_scroll_delta = 0};
+ }}
+ else
+ screen:expect{grid=[[
+ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{19: }{1:^ }{5:│}{0: }|
+ {0:~ }{5:│}{19:+}{28:+-- 2 lines: ·····}{5:│}{0: }|
+ {0:~ }{5:│}{2:~ }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ |
+ ]]}
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 2, 1)
+ screen:expect{grid=[[
+ ## grid 1
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [2:----------------------------------------]|
+ [3:----------------------------------------]|
+ ## grid 2
+ |
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ {0:~ }|
+ ## grid 3
+ |
+ ## grid 4
+ {5:┌────────────────────┐}|
+ {5:│}{19: }{1:^ }{5:│}|
+ {5:│}{19:-}{1: }{5:│}|
+ {5:│}{19:│}{1: }{5:│}|
+ {5:└────────────────────┘}|
+ ]], float_pos={
+ [4] = {{id = 1001}, "NW", 1, 0, 5, true, 50};
+ }, win_viewport={
+ [2] = {win = {id = 1000}, topline = 0, botline = 2, curline = 0, curcol = 0, linecount = 1, sum_scroll_delta = 0};
+ [4] = {win = {id = 1001}, topline = 0, botline = 3, curline = 0, curcol = 0, linecount = 3, sum_scroll_delta = 0};
+ }}
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 6)
+ screen:expect{grid=[[
+ {5:┌────────────────────┐} |
+ {0:~ }{5:│}{19: }{1:^ }{5:│}{0: }|
+ {0:~ }{5:│}{19:-}{1: }{5:│}{0: }|
+ {0:~ }{5:│}{19:│}{1: }{5:│}{0: }|
+ {0:~ }{5:└────────────────────┘}{0: }|
+ {0:~ }|
+ |
+ ]]}
+ end
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 2, 2)
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 7)
+ end
+ eq({0, 2, 1, 0, 1}, funcs.getcurpos())
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 2, 3)
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 8)
+ end
+ eq({0, 2, 1, 0, 2}, funcs.getcurpos())
+
+ if multigrid then
+ meths.input_mouse('left', 'press', '', 4, 2, 11)
+ else
+ meths.input_mouse('left', 'press', '', 0, 2, 16)
+ end
+ eq({0, 2, 1, 0, 10}, funcs.getcurpos())
+ end)
+
it("'winblend' option", function()
screen:try_resize(50,9)
screen:set_default_attr_ids({
diff --git a/test/functional/ui/fold_spec.lua b/test/functional/ui/fold_spec.lua
index 5e907f82ba..f42682ba69 100644
--- a/test/functional/ui/fold_spec.lua
+++ b/test/functional/ui/fold_spec.lua
@@ -33,7 +33,7 @@ describe("folded lines", function()
[1] = {bold = true, foreground = Screen.colors.Blue1},
[2] = {reverse = true},
[3] = {bold = true, reverse = true},
- [4] = {foreground = Screen.colors.Grey100, background = Screen.colors.Red},
+ [4] = {foreground = Screen.colors.White, background = Screen.colors.Red},
[5] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.LightGrey},
[6] = {background = Screen.colors.Yellow},
[7] = {foreground = Screen.colors.DarkBlue, background = Screen.colors.WebGray},
@@ -2938,10 +2938,15 @@ describe("folded lines", function()
screen:try_resize(30, 7)
insert(content1)
command("hi! CursorLine guibg=NONE guifg=Red gui=NONE")
+ command('hi F0 guibg=Red guifg=Black')
+ command('hi F1 guifg=White')
meths.set_option_value('cursorline', true, {})
meths.set_option_value('foldcolumn', '4', {})
- meths.set_option_value('foldtext',
- '[[v:folddashes], ["\t", "Search"], [getline(v:foldstart), "NonText"]]', {})
+ meths.set_option_value('foldtext', '['
+ .. '["▶", ["F0", "F1"]], '
+ .. '[v:folddashes], '
+ .. '["\t", "Search"], '
+ .. '[getline(v:foldstart), "NonText"]]', {})
command('3,4fold')
command('5,6fold')
@@ -2958,7 +2963,7 @@ describe("folded lines", function()
[3:------------------------------]|
## grid 2
{7: }This is a |
- {7:+ }{13:^-}{17: }{18:valid English}{13:·····}|
+ {7:+ }{4:^▶}{13:-}{17: }{18:valid English}{13:·····}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -2969,7 +2974,7 @@ describe("folded lines", function()
else
screen:expect([[
{7: }This is a |
- {7:+ }{13:^-}{17: }{18:valid English}{13:·····}|
+ {7:+ }{4:^▶}{13:-}{17: }{18:valid English}{13:·····}|
{1:~ }|
{1:~ }|
{1:~ }|
@@ -2977,7 +2982,7 @@ describe("folded lines", function()
|
]])
end
- eq('-\tvalid English', funcs.foldtextresult(2))
+ eq('▶-\tvalid English', funcs.foldtextresult(2))
feed('zo')
if multigrid then
@@ -2993,8 +2998,8 @@ describe("folded lines", function()
## grid 2
{7: }This is a |
{7:- }valid English |
- {7:│+ }{5:--}{19: }{18:sentence composed }|
- {7:│+ }{13:^--}{17: }{18:in his cave.}{13:······}|
+ {7:│+ }{4:▶}{5:--}{19: }{18:sentence composed }|
+ {7:│+ }{4:^▶}{13:--}{17: }{18:in his cave.}{13:······}|
{1:~ }|
{1:~ }|
## grid 3
@@ -3004,15 +3009,15 @@ describe("folded lines", function()
screen:expect([[
{7: }This is a |
{7:- }valid English |
- {7:│+ }{5:--}{19: }{18:sentence composed }|
- {7:│+ }{13:^--}{17: }{18:in his cave.}{13:······}|
+ {7:│+ }{4:▶}{5:--}{19: }{18:sentence composed }|
+ {7:│+ }{4:^▶}{13:--}{17: }{18:in his cave.}{13:······}|
{1:~ }|
{1:~ }|
|
]])
end
- eq('--\tsentence composed by', funcs.foldtextresult(3))
- eq('--\tin his cave.', funcs.foldtextresult(5))
+ eq('▶--\tsentence composed by', funcs.foldtextresult(3))
+ eq('▶--\tin his cave.', funcs.foldtextresult(5))
command('hi! Visual guibg=Red')
feed('V2k')
@@ -3029,8 +3034,8 @@ describe("folded lines", function()
## grid 2
{7: }This is a |
{7:- }^v{14:alid English} |
- {7:│+ }{15:--}{19: }{20:sentence composed }|
- {7:│+ }{15:--}{19: }{20:in his cave.}{15:······}|
+ {7:│+ }{4:▶}{15:--}{19: }{20:sentence composed }|
+ {7:│+ }{4:▶}{15:--}{19: }{20:in his cave.}{15:······}|
{1:~ }|
{1:~ }|
## grid 3
@@ -3040,8 +3045,8 @@ describe("folded lines", function()
screen:expect([[
{7: }This is a |
{7:- }^v{14:alid English} |
- {7:│+ }{15:--}{19: }{20:sentence composed }|
- {7:│+ }{15:--}{19: }{20:in his cave.}{15:······}|
+ {7:│+ }{4:▶}{15:--}{19: }{20:sentence composed }|
+ {7:│+ }{4:▶}{15:--}{19: }{20:in his cave.}{15:······}|
{1:~ }|
{1:~ }|
{11:-- VISUAL LINE --} |
@@ -3062,8 +3067,8 @@ describe("folded lines", function()
## grid 2
a si sihT{7: }|
{14:hsilgnE dila}^v{7: -}|
- {20: desopmoc ecnetnes}{19: }{15:--}{7: +│}|
- {15:······}{20:.evac sih ni}{19: }{15:--}{7: +│}|
+ {20: desopmoc ecnetnes}{19: }{15:--}{4:▶}{7: +│}|
+ {15:······}{20:.evac sih ni}{19: }{15:--}{4:▶}{7: +│}|
{1: ~}|
{1: ~}|
## grid 3
@@ -3073,8 +3078,8 @@ describe("folded lines", function()
screen:expect([[
a si sihT{7: }|
{14:hsilgnE dila}^v{7: -}|
- {20: desopmoc ecnetnes}{19: }{15:--}{7: +│}|
- {15:······}{20:.evac sih ni}{19: }{15:--}{7: +│}|
+ {20: desopmoc ecnetnes}{19: }{15:--}{4:▶}{7: +│}|
+ {15:······}{20:.evac sih ni}{19: }{15:--}{4:▶}{7: +│}|
{1: ~}|
{1: ~}|
{11:-- VISUAL LINE --} |
diff --git a/test/functional/ui/mouse_spec.lua b/test/functional/ui/mouse_spec.lua
index a8d01cfbf1..fd24174f74 100644
--- a/test/functional/ui/mouse_spec.lua
+++ b/test/functional/ui/mouse_spec.lua
@@ -1676,7 +1676,7 @@ describe('ui/mouse/input', function()
end)
- it('getmousepos works correctly', function()
+ it('getmousepos() works correctly', function()
local winwidth = meths.get_option_value('winwidth', {})
-- Set winwidth=1 so that window sizes don't change.
meths.set_option_value('winwidth', 1, {})
@@ -1771,7 +1771,7 @@ describe('ui/mouse/input', function()
-- Test that mouse position values are properly set for ordinary windows.
-- Set the float to be unfocusable instead of closing, to additionally test
- -- that getmousepos does not consider unfocusable floats. (see discussion
+ -- that getmousepos() does not consider unfocusable floats. (see discussion
-- in PR #14937 for details).
opts.focusable = false
meths.win_set_config(float, opts)
diff --git a/test/functional/ui/multigrid_spec.lua b/test/functional/ui/multigrid_spec.lua
index 1778c8218b..6f4082beda 100644
--- a/test/functional/ui/multigrid_spec.lua
+++ b/test/functional/ui/multigrid_spec.lua
@@ -2307,6 +2307,7 @@ describe('ext_multigrid', function()
{1:~ }|
]]}
+ -- XXX: mouse_check_grid() doesn't work properly when clicking on grid 1
meths.input_mouse('left', 'press', '', 1, 6, 20)
-- TODO(bfredl): "batching" input_mouse is formally not supported yet.
-- Normally it should work fine in async context when nvim is not blocked,
diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim
index 3c21041899..09d3f0432f 100644
--- a/test/old/testdir/test_normal.vim
+++ b/test/old/testdir/test_normal.vim
@@ -4107,6 +4107,39 @@ func Test_normal_click_on_double_width_char()
let &mouse = save_mouse
endfunc
+func Test_normal_click_on_empty_line()
+ let save_mouse = &mouse
+ set mouse=a
+ botright new
+ call setline(1, ['', '', ''])
+ let row = win_screenpos(0)[0] + 2
+ 20vsplit
+ redraw
+
+ call Ntest_setmouse(row, 1)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 1], getcurpos())
+ call Ntest_setmouse(row, 2)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 2], getcurpos())
+ call Ntest_setmouse(row, 10)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 10], getcurpos())
+
+ call Ntest_setmouse(row, 21 + 1)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 1], getcurpos())
+ call Ntest_setmouse(row, 21 + 2)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 2], getcurpos())
+ call Ntest_setmouse(row, 21 + 10)
+ call feedkeys("\<LeftMouse>", 'xt')
+ call assert_equal([0, 3, 1, 0, 10], getcurpos())
+
+ bwipe!
+ let &mouse = save_mouse
+endfunc
+
func Test_normal33_g_cmd_nonblank()
" Test that g<End> goes to the last non-blank char and g$ to the last
" visible column
diff --git a/test/old/testdir/test_virtualedit.vim b/test/old/testdir/test_virtualedit.vim
index 6ff51e36fb..4ab69d89fe 100644
--- a/test/old/testdir/test_virtualedit.vim
+++ b/test/old/testdir/test_virtualedit.vim
@@ -564,37 +564,95 @@ func Test_virtualedit_mouse()
let save_mouse = &mouse
set mouse=a
set virtualedit=all
- new
+ botright new
+ let row = win_screenpos(0)[0]
+ 20vsplit
+ wincmd p
call setline(1, ["text\tword"])
redraw
- call Ntest_setmouse(1, 4)
+ call Ntest_setmouse(row, 21 + 4)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 4, 0, 4], getcurpos())
- call Ntest_setmouse(1, 5)
+ call Ntest_setmouse(row, 21 + 5)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 5, 0, 5], getcurpos())
- call Ntest_setmouse(1, 6)
+ call Ntest_setmouse(row, 21 + 6)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 5, 1, 6], getcurpos())
- call Ntest_setmouse(1, 7)
+ call Ntest_setmouse(row, 21 + 7)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 5, 2, 7], getcurpos())
- call Ntest_setmouse(1, 8)
+ call Ntest_setmouse(row, 21 + 8)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 5, 3, 8], getcurpos())
- call Ntest_setmouse(1, 9)
+ call Ntest_setmouse(row, 21 + 9)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 6, 0, 9], getcurpos())
- call Ntest_setmouse(1, 12)
+ call Ntest_setmouse(row, 21 + 12)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 9, 0, 12], getcurpos())
- call Ntest_setmouse(1, 13)
+ call Ntest_setmouse(row, 21 + 13)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 10, 0, 13], getcurpos())
- call Ntest_setmouse(1, 15)
+ call Ntest_setmouse(row, 21 + 15)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 2, 15], getcurpos())
+
+ setlocal nowrap
+ call setline(2, repeat('a', 19))
+ normal! j14zl
+ redraw
+ call Ntest_setmouse(row, 21 + 1)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 2, 15], getcurpos())
+ call Ntest_setmouse(row, 21 + 11)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 12, 25], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 1)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 15, 0, 15], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 11)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 20, 5, 25], getcurpos())
+
+ setlocal number numberwidth=2
+ redraw
+ call Ntest_setmouse(row, 21 + 3)
call feedkeys("\<LeftMouse>", "xt")
call assert_equal([0, 1, 10, 2, 15], getcurpos())
+ call Ntest_setmouse(row, 21 + 13)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 12, 25], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 3)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 15, 0, 15], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 13)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 20, 5, 25], getcurpos())
+ setlocal nonumber
+
+ if has('signs')
+ sign define Sign1 text=口
+ sign place 1 name=Sign1 line=1
+ sign place 2 name=Sign1 line=2
+ redraw
+ call Ntest_setmouse(row, 21 + 3)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 2, 15], getcurpos())
+ call Ntest_setmouse(row, 21 + 13)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 1, 10, 12, 25], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 3)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 15, 0, 15], getcurpos())
+ call Ntest_setmouse(row + 1, 21 + 13)
+ call feedkeys("\<LeftMouse>", "xt")
+ call assert_equal([0, 2, 20, 5, 25], getcurpos())
+ sign unplace 1
+ sign unplace 2
+ sign undefine Sign1
+ endif
bwipe!
let &mouse = save_mouse