diff options
30 files changed, 779 insertions, 262 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/motion.txt b/runtime/doc/motion.txt index dc92601bfc..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!". @@ -1115,60 +1115,44 @@ If you have included the ' item in the 'shada' option the jumplist will be stored in the ShaDa file and restored when starting Vim. *jumplist-stack* -When jumpoptions includes "stack", the jumplist behaves like the history in a -web browser and like the tag stack. When jumping to a new location from the -middle of the jumplist, the locations after the current position will be -discarded. - -This behavior corresponds to the following situation in a web browser. -Navigate to first.com, second.com, third.com, fourth.com and then fifth.com. -Then navigate backwards twice so that third.com is displayed. At that point, -the history is: -- first.com -- second.com -- third.com <-- -- fourth.com -- fifth.com - -Finally, navigate to a different webpage, new.com. The history is -- first.com -- second.com -- third.com -- new.com <-- - -When the jumpoptions includes "stack", this is the behavior of Nvim as well. -That is, given a jumplist like the following in which CTRL-O has been used to -move back three times to location X - - jump line col file/text - 2 1260 8 src/nvim/mark.c <-- location X-2 - 1 685 0 src/nvim/option_defs.h <-- location X-1 -> 0 462 36 src/nvim/option_defs.h <-- location X - 1 479 39 src/nvim/option_defs.h - 2 213 2 src/nvim/mark.c - 3 181 0 src/nvim/mark.c - +When 'jumpoptions' option includes "stack", the jumplist behaves like the tag +stack. When jumping to a new location from the middle of the jumplist, the +locations after the current position will be discarded. With this option set +you can move through a tree of jump locations. When going back up a branch and +then down another branch, CTRL-O still takes you further up the tree. + +Given a jumplist like the following in which CTRL-O has been used to move back +three times to location X: > + + jump line col file/text + 2 1260 8 mark.c <-- location X-2 + 1 685 0 eval.c <-- location X-1 + > 0 462 36 eval.c <-- location X + 1 479 39 eval.c + 2 213 2 mark.c + 3 181 0 mark.c +< jumping to (new) location Y results in the locations after the current -locations being removed: - - jump line col file/text - 3 1260 8 src/nvim/mark.c - 2 685 0 src/nvim/option_defs.h - 1 462 36 src/nvim/option_defs.h <-- location X -> +locations being removed: > + jump line col file/text + 3 1260 8 mark.c <-- location X-2 + 2 685 0 eval.c <-- location X-1 + 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 it -prior to the original jump from X to Y: - - jump line col file/text - 4 1260 8 src/nvim/mark.c <-- location X-2 - 3 685 0 src/nvim/option_defs.h <-- location X-1 - 2 462 36 src/nvim/option_defs.h <-- location X - 1 100 0 src/nvim/option_defs.h <-- location Y -> - +position relative to the locations (X-1, X-2, etc., ...) that had been before +it prior to the original jump from X to Y: > + + jump line col file/text + 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 + > +< CHANGE LIST JUMPS *changelist* *change-list-jumps* *E664* When making a change the cursor position is remembered. One position is diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 96bf2eef63..deb64bf18d 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3569,12 +3569,11 @@ A jump table for the options with a short description can be found at |Q_op|. 'jumpoptions' 'jop' string (default "") global List of words that change the behavior of the |jumplist|. - stack Make the jumplist behave like the tagstack or like a - web browser. Relative location of entries in the - jumplist is preserved at the cost of discarding - subsequent entries when navigating backwards in the - jumplist and then jumping to a location. - |jumplist-stack| + stack Make the jumplist behave like the tagstack. + Relative location of entries in the jumplist is + preserved at the cost of discarding subsequent entries + when navigating backwards in the jumplist and then + jumping to a location. |jumplist-stack| view When moving through the jumplist, |changelist|, |alternate-file| or using |mark-motions| try to diff --git a/runtime/lua/vim/_editor.lua b/runtime/lua/vim/_editor.lua index 0215cae0cb..8322777633 100644 --- a/runtime/lua/vim/_editor.lua +++ b/runtime/lua/vim/_editor.lua @@ -29,6 +29,7 @@ for k, v in pairs({ treesitter = true, filetype = true, loader = true, + func = true, F = true, lsp = true, highlight = true, diff --git a/runtime/lua/vim/_meta.lua b/runtime/lua/vim/_meta.lua index 5e4f390ca3..ddd0a0eb49 100644 --- a/runtime/lua/vim/_meta.lua +++ b/runtime/lua/vim/_meta.lua @@ -10,6 +10,7 @@ vim._watch = require('vim._watch') vim.diagnostic = require('vim.diagnostic') vim.filetype = require('vim.filetype') vim.fs = require('vim.fs') +vim.func = require('vim.func') vim.health = require('vim.health') vim.highlight = require('vim.highlight') vim.iter = require('vim.iter') diff --git a/runtime/lua/vim/_meta/options.lua b/runtime/lua/vim/_meta/options.lua index 734096a755..b8bdfbc3f7 100644 --- a/runtime/lua/vim/_meta/options.lua +++ b/runtime/lua/vim/_meta/options.lua @@ -3485,12 +3485,11 @@ vim.go.joinspaces = vim.o.joinspaces vim.go.js = vim.go.joinspaces --- List of words that change the behavior of the `jumplist`. ---- stack Make the jumplist behave like the tagstack or like a ---- web browser. Relative location of entries in the ---- jumplist is preserved at the cost of discarding ---- subsequent entries when navigating backwards in the ---- jumplist and then jumping to a location. ---- `jumplist-stack` +--- stack Make the jumplist behave like the tagstack. +--- Relative location of entries in the jumplist is +--- preserved at the cost of discarding subsequent entries +--- when navigating backwards in the jumplist and then +--- jumping to a location. `jumplist-stack` --- --- view When moving through the jumplist, `changelist|, --- |alternate-file` or using `mark-motions` try to diff --git a/runtime/lua/vim/filetype.lua b/runtime/lua/vim/filetype.lua index c7f025f9e7..5ae4e508ef 100644 --- a/runtime/lua/vim/filetype.lua +++ b/runtime/lua/vim/filetype.lua @@ -210,6 +210,7 @@ local extension = { astro = 'astro', atl = 'atlas', as = 'atlas', + zed = 'authzed', ahk = 'autohotkey', au3 = 'autoit', ave = 'ave', diff --git a/runtime/lua/vim/func.lua b/runtime/lua/vim/func.lua new file mode 100644 index 0000000000..206d1bae95 --- /dev/null +++ b/runtime/lua/vim/func.lua @@ -0,0 +1,41 @@ +local M = {} + +-- TODO(lewis6991): Private for now until: +-- - There are other places in the codebase that could benefit from this +-- (e.g. LSP), but might require other changes to accommodate. +-- - Invalidation of the cache needs to be controllable. Using weak tables +-- is an acceptable invalidation policy, but it shouldn't be the only +-- one. +-- - I don't think the story around `hash` is completely thought out. We +-- may be able to have a good default hash by hashing each argument, +-- so basically a better 'concat'. +-- - Need to support multi level caches. Can be done by allow `hash` to +-- return multiple values. +-- +--- Memoizes a function {fn} using {hash} to hash the arguments. +--- +--- Internally uses a |lua-weaktable| to cache the results of {fn} meaning the +--- cache will be invalidated whenever Lua does garbage collection. +--- +--- The memoized function returns shared references so be wary about +--- mutating return values. +--- +--- @generic F: function +--- @param hash integer|string|function Hash function to create a hash to use as a key to +--- store results. Possible values: +--- - When integer, refers to the index of an argument of {fn} to hash. +--- This argument can have any type. +--- - When function, is evaluated using the same arguments passed to {fn}. +--- - When `concat`, the hash is determined by string concatenating all the +--- arguments passed to {fn}. +--- - When `concat-n`, the hash is determined by string concatenating the +--- first n arguments passed to {fn}. +--- +--- @param fn F Function to memoize. +--- @return F # Memoized version of {fn} +--- @nodoc +function M._memoize(hash, fn) + return require('vim.func._memoize')(hash, fn) +end + +return M diff --git a/runtime/lua/vim/func/_memoize.lua b/runtime/lua/vim/func/_memoize.lua new file mode 100644 index 0000000000..835bf64c93 --- /dev/null +++ b/runtime/lua/vim/func/_memoize.lua @@ -0,0 +1,59 @@ +--- Module for private utility functions + +--- @param argc integer? +--- @return fun(...): any +local function concat_hash(argc) + return function(...) + return table.concat({ ... }, '%%', 1, argc) + end +end + +--- @param idx integer +--- @return fun(...): any +local function idx_hash(idx) + return function(...) + return select(idx, ...) + end +end + +--- @param hash integer|string|fun(...): any +--- @return fun(...): any +local function resolve_hash(hash) + if type(hash) == 'number' then + hash = idx_hash(hash) + elseif type(hash) == 'string' then + local c = hash == 'concat' or hash:match('^concat%-(%d+)') + if c then + hash = concat_hash(tonumber(c)) + else + error('invalid value for hash: ' .. hash) + end + end + --- @cast hash -integer + return hash +end + +--- @generic F: function +--- @param hash integer|string|fun(...): any +--- @param fn F +--- @return F +return function(hash, fn) + vim.validate({ + hash = { hash, { 'number', 'string', 'function' } }, + fn = { fn, 'function' }, + }) + + ---@type table<any,table<any,any>> + local cache = setmetatable({}, { __mode = 'kv' }) + + hash = resolve_hash(hash) + + return function(...) + local key = hash(...) + if cache[key] == nil then + cache[key] = vim.F.pack_len(fn(...)) + end + + return vim.F.unpack_len(cache[key]) + end +end diff --git a/runtime/lua/vim/treesitter/_query_linter.lua b/runtime/lua/vim/treesitter/_query_linter.lua index abf0bf345d..87d74789a3 100644 --- a/runtime/lua/vim/treesitter/_query_linter.lua +++ b/runtime/lua/vim/treesitter/_query_linter.lua @@ -10,20 +10,12 @@ local M = {} --- @alias vim.treesitter.ParseError {msg: string, range: Range4} ---- @private ---- Caches parse results for queries for each language. ---- Entries of parse_cache[lang][query_text] will either be true for successful parse or contain the ---- message and range of the parse error. ---- @type table<string,table<string,vim.treesitter.ParseError|true>> -local parse_cache = {} - --- Contains language dependent context for the query linter --- @class QueryLinterLanguageContext --- @field lang string? Current `lang` of the targeted parser --- @field parser_info table? Parser info returned by vim.treesitter.language.inspect --- @field is_first_lang boolean Whether this is the first language of a linter run checking queries for multiple `langs` ---- @private --- Adds a diagnostic for node in the query buffer --- @param diagnostics Diagnostic[] --- @param range Range4 @@ -42,7 +34,6 @@ local function add_lint_for_node(diagnostics, range, lint, lang) } end ---- @private --- Determines the target language of a query file by its path: <lang>/<query_type>.scm --- @param buf integer --- @return string? @@ -53,7 +44,6 @@ local function guess_query_lang(buf) end end ---- @private --- @param buf integer --- @param opts QueryLinterOpts|QueryLinterNormalizedOpts|nil --- @return QueryLinterNormalizedOpts @@ -87,7 +77,6 @@ local lint_query = [[;; query (ERROR) @error ]] ---- @private --- @param err string --- @param node TSNode --- @return vim.treesitter.ParseError @@ -112,38 +101,26 @@ local function get_error_entry(err, node) } end ---- @private --- @param node TSNode --- @param buf integer --- @param lang string ---- @param diagnostics Diagnostic[] -local function check_toplevel(node, buf, lang, diagnostics) - local query_text = vim.treesitter.get_node_text(node, buf) - - if not parse_cache[lang] then - parse_cache[lang] = {} - end - - local lang_cache = parse_cache[lang] - - if lang_cache[query_text] == nil then - local cache_val, err = pcall(vim.treesitter.query.parse, lang, query_text) ---@type boolean|vim.treesitter.ParseError, string|Query - - if not cache_val and type(err) == 'string' then - cache_val = get_error_entry(err, node) - end - - lang_cache[query_text] = cache_val - end +local function hash_parse(node, buf, lang) + return tostring(node:id()) .. tostring(buf) .. tostring(vim.b[buf].changedtick) .. lang +end - local cache_entry = lang_cache[query_text] +--- @param node TSNode +--- @param buf integer +--- @param lang string +--- @return vim.treesitter.ParseError? +local parse = vim.func._memoize(hash_parse, function(node, buf, lang) + local query_text = vim.treesitter.get_node_text(node, buf) + local ok, err = pcall(vim.treesitter.query.parse, lang, query_text) ---@type boolean|vim.treesitter.ParseError, string|Query - if type(cache_entry) ~= 'boolean' then - add_lint_for_node(diagnostics, cache_entry.range, cache_entry.msg, lang) + if not ok and type(err) == 'string' then + return get_error_entry(err, node) end -end +end) ---- @private --- @param buf integer --- @param match table<integer,TSNode> --- @param query Query @@ -164,7 +141,10 @@ local function lint_match(buf, match, query, lang_context, diagnostics) -- other checks rely on Neovim parser introspection if lang and parser_info and cap_id == 'toplevel' then - check_toplevel(node, buf, lang, diagnostics) + local err = parse(node, buf, lang) + if err then + add_lint_for_node(diagnostics, err.range, err.msg, lang) + end end end end diff --git a/runtime/lua/vim/treesitter/languagetree.lua b/runtime/lua/vim/treesitter/languagetree.lua index f931291ed7..b2c4e9167d 100644 --- a/runtime/lua/vim/treesitter/languagetree.lua +++ b/runtime/lua/vim/treesitter/languagetree.lua @@ -738,12 +738,14 @@ local function add_injection(t, tree_index, pattern, lang, combined, ranges) end -- TODO(clason): replace by refactored `ts.has_parser` API (without registering) ----@param lang string parser name ----@return boolean # true if parser for {lang} exists on rtp -local has_parser = function(lang) +--- The result of this function is cached to prevent nvim_get_runtime_file from being +--- called too often +--- @param lang string parser name +--- @return boolean # true if parser for {lang} exists on rtp +local has_parser = vim.func._memoize(1, function(lang) return vim._ts_has_language(lang) or #vim.api.nvim_get_runtime_file('parser/' .. lang .. '.*', false) > 0 -end +end) --- Return parser name for language (if exists) or filetype (if registered and exists). --- Also attempts with the input lower-cased. diff --git a/runtime/lua/vim/treesitter/query.lua b/runtime/lua/vim/treesitter/query.lua index 313d837d5c..8cbbffcd60 100644 --- a/runtime/lua/vim/treesitter/query.lua +++ b/runtime/lua/vim/treesitter/query.lua @@ -191,12 +191,6 @@ function M.set(lang, query_name, text) explicit_queries[lang][query_name] = M.parse(lang, text) end ---- `false` if query files didn't exist or were empty ----@type table<string, table<string, Query|false>> -local query_get_cache = vim.defaulttable(function() - return setmetatable({}, { __mode = 'v' }) -end) - ---@deprecated function M.get_query(...) vim.deprecate('vim.treesitter.query.get_query()', 'vim.treesitter.query.get()', '0.10') @@ -209,34 +203,19 @@ end ---@param query_name string Name of the query (e.g. "highlights") --- ---@return Query|nil Parsed query -function M.get(lang, query_name) +M.get = vim.func._memoize('concat-2', function(lang, query_name) if explicit_queries[lang][query_name] then return explicit_queries[lang][query_name] end - local cached = query_get_cache[lang][query_name] - if cached then - return cached - elseif cached == false then - return nil - end - local query_files = M.get_files(lang, query_name) local query_string = read_query_files(query_files) if #query_string == 0 then - query_get_cache[lang][query_name] = false return nil end - local query = M.parse(lang, query_string) - query_get_cache[lang][query_name] = query - return query -end - ----@type table<string, table<string, Query>> -local query_parse_cache = vim.defaulttable(function() - return setmetatable({}, { __mode = 'v' }) + return M.parse(lang, query_string) end) ---@deprecated @@ -262,20 +241,15 @@ end ---@param query string Query in s-expr syntax --- ---@return Query Parsed query -function M.parse(lang, query) +M.parse = vim.func._memoize('concat-2', function(lang, query) language.add(lang) - local cached = query_parse_cache[lang][query] - if cached then - return cached - end local self = setmetatable({}, Query) self.query = vim._ts_parse_query(lang, query) self.info = self.query:inspect() self.captures = self.info.captures - query_parse_cache[lang][query] = self return self -end +end) ---@deprecated function M.get_range(...) diff --git a/scripts/lua2dox.lua b/scripts/lua2dox.lua index 7594a0e7df..1c8bc5a3cb 100644 --- a/scripts/lua2dox.lua +++ b/scripts/lua2dox.lua @@ -361,6 +361,12 @@ local function process_line(line, in_stream, generics) return process_block_comment(line:sub(5), in_stream) end + -- Hax... I'm sorry + -- M.fun = vim.memoize(function(...) + -- -> + -- function M.fun(...) + line = line:gsub('^(.+) = .*_memoize%([^,]+, function%((.*)%)$', 'function %1(%2)') + if line:find('^function') or line:find('^local%s+function') then return process_function_header(line) end diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index 82a88c6774..94e5e6ee28 100644 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -879,7 +879,13 @@ set(VIMDOC_FILES ) glob_wrapper(API_SOURCES ${PROJECT_SOURCE_DIR}/src/nvim/api/*.c) -glob_wrapper(LUA_SOURCES ${NVIM_RUNTIME_DIR}/lua/vim/*.lua) + +glob_wrapper(LUA_SOURCES + ${NVIM_RUNTIME_DIR}/lua/vim/*.lua + ${NVIM_RUNTIME_DIR}/lua/vim/filetype/*.lua + ${NVIM_RUNTIME_DIR}/lua/vim/lsp/*.lua + ${NVIM_RUNTIME_DIR}/lua/vim/treesitter/*.lua +) add_custom_command( OUTPUT ${VIMDOC_FILES} diff --git a/src/nvim/decoration.c b/src/nvim/decoration.c index f4ca31040a..b70f070a51 100644 --- a/src/nvim/decoration.c +++ b/src/nvim/decoration.c @@ -254,7 +254,7 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r DecorRange range = { start_row, start_col, end_row, end_col, *decor, attr_id, - kv_size(decor->virt_text) && owned, -1, ns_id, mark_id }; + kv_size(decor->virt_text) && owned, -10, ns_id, mark_id }; kv_pushp(state->active); size_t index; @@ -268,10 +268,10 @@ static void decor_add(DecorState *state, int start_row, int start_col, int end_r kv_A(state->active, index) = range; } -/// Initialize the draw_col of a newly-added non-inline virtual text item. +/// Initialize the draw_col of a newly-added virtual text item. static void decor_init_draw_col(int win_col, bool hidden, DecorRange *item) { - if (win_col < 0) { + if (win_col < 0 && item->decor.virt_text_pos != kVTInline) { item->draw_col = win_col; } else if (item->decor.virt_text_pos == kVTOverlay) { item->draw_col = (item->decor.virt_text_hide && hidden) ? INT_MIN : win_col; @@ -371,8 +371,7 @@ next_mark: spell = item.decor.spell; } if (item.start_row == state->row && item.start_col <= col - && decor_virt_pos(&item.decor) && item.draw_col == -1 - && item.decor.virt_text_pos != kVTInline) { + && decor_virt_pos(&item.decor) && item.draw_col == -10) { decor_init_draw_col(win_col, hidden, &item); } if (keep) { diff --git a/src/nvim/decoration.h b/src/nvim/decoration.h index 0f191aa870..fef5ff7c2a 100644 --- a/src/nvim/decoration.h +++ b/src/nvim/decoration.h @@ -85,6 +85,7 @@ typedef struct { /// Screen column to draw the virtual text. /// When -1, the virtual text may be drawn after deciding where. /// When -3, the virtual text should be drawn on the next screen line. + /// When -10, the virtual text has just been added. /// When INT_MIN, the virtual text should no longer be drawn. int draw_col; uint64_t ns_id; diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index 5fda200a43..73021d83a8 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -223,7 +223,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b if (*p == TAB) { cells = MIN(tabstop_padding(vcol, buf->b_p_ts, buf->b_p_vts_array), maxcells); for (int c = 0; c < cells; c++) { - dest[c] = schar_from_ascii(' '); + dest[rl ? -c : c] = schar_from_ascii(' '); } goto done; } else if ((uint8_t)(*p) < 0x80 && u8cc[0] == 0) { @@ -257,7 +257,7 @@ static int line_putchar(buf_T *buf, LineState *s, schar_T *dest, int maxcells, b dest[0] = schar_from_cc(u8c, u8cc); } if (cells > 1) { - dest[1] = 0; + dest[rl ? -1 : 1] = 0; } done: s->p += c_len; @@ -277,12 +277,20 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int } if (item->draw_col == -1) { if (item->decor.virt_text_pos == kVTRightAlign) { - right_pos -= item->decor.virt_text_width; + if (wp->w_p_rl) { + right_pos += item->decor.virt_text_width; + } else { + right_pos -= item->decor.virt_text_width; + } item->draw_col = right_pos; } else if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { item->draw_col = state->eol_col; } else if (item->decor.virt_text_pos == kVTWinCol) { - item->draw_col = MAX(item->decor.col + col_off, 0); + if (wp->w_p_rl) { + item->draw_col = MIN(col_off - item->decor.col, wp->w_grid.cols - 1); + } else { + item->draw_col = MAX(col_off + item->decor.col, 0); + } } } if (item->draw_col < 0) { @@ -297,25 +305,33 @@ static void draw_virt_text(win_T *wp, buf_T *buf, int col_off, int *end_col, int } if (kv_size(item->decor.virt_text)) { col = draw_virt_text_item(buf, item->draw_col, item->decor.virt_text, - item->decor.hl_mode, max_col, item->draw_col - col_off); + item->decor.hl_mode, max_col, item->draw_col - col_off, wp->w_p_rl); } item->draw_col = INT_MIN; // deactivate if (item->decor.virt_text_pos == kVTEndOfLine && do_eol) { - state->eol_col = col + 1; + if (wp->w_p_rl) { + state->eol_col = col - 1; + } else { + state->eol_col = col + 1; + } } - *end_col = MAX(*end_col, col); + if (wp->w_p_rl) { + *end_col = MIN(*end_col, col); + } else { + *end_col = MAX(*end_col, col); + } } } static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, int max_col, - int vcol) + int vcol, bool rl) { LineState s = LINE_STATE(""); int virt_attr = 0; size_t virt_pos = 0; - while (col < max_col) { + while (rl ? col > max_col : col < max_col) { if (!*s.p) { if (virt_pos >= kv_size(vt)) { break; @@ -346,20 +362,27 @@ static int draw_virt_text_item(buf_T *buf, int col, VirtText vt, HlMode hl_mode, attr = virt_attr; } schar_T dummy[2]; + bool rl_overwrote_double_width = linebuf_char[col] == 0; int cells = line_putchar(buf, &s, through ? dummy : &linebuf_char[col], - max_col - col, false, vcol); + rl ? col - max_col : max_col - col, rl, vcol); // If we failed to emit a char, we still need to put a space and advance. if (cells < 1) { linebuf_char[col] = schar_from_ascii(' '); cells = 1; } for (int c = 0; c < cells; c++) { - linebuf_attr[col++] = attr; + linebuf_attr[col] = attr; + if (rl) { + col--; + } else { + col++; + } } - if (col < max_col && linebuf_char[col] == 0) { - // If the left half of a double-width char is overwritten, - // change the right half to a space so that grid redraws properly, - // but don't advance the current column. + // If one half of a double-width char is overwritten, + // change the other half to a space so that grid redraws properly, + // but don't advance the current column. + if ((rl && col > max_col && rl_overwrote_double_width) + || (!rl && col < max_col && linebuf_char[col] == 0)) { linebuf_char[col] = schar_from_ascii(' '); } vcol += cells; @@ -1387,8 +1410,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl statuscol.width = win_col_off(wp) - (cmdwin_type != 0 && wp == curwin); statuscol.use_cul = use_cursor_line_highlight(wp, lnum); statuscol.sign_cul_id = statuscol.use_cul ? sign_cul.hl_id : 0; - statuscol.num_attr = sign_num.hl_id ? syn_id2attr(sign_num.hl_id) - : get_line_number_attr(wp, &wlv); + statuscol.num_attr = sign_num.hl_id > 0 ? syn_id2attr(sign_num.hl_id) : 0; } else { if (sign_cul.hl_id > 0) { sign_cul_attr = syn_id2attr(sign_cul.hl_id); @@ -1675,13 +1697,16 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl virt_line_offset = kv_A(virt_lines, virt_line_index).left_col ? 0 : win_col_off(wp); } } - if (!virt_line_offset) { + if (virt_line_offset == 0) { // Skip the column states if there is a "virt_left_col" line. wlv.draw_state = WL_BRI - 1; } else if (statuscol.draw) { // Skip fold, sign and number states if 'statuscolumn' is set. wlv.draw_state = WL_STC - 1; } + if (virt_line_offset >= 0 && wp->w_p_rl) { + virt_line_offset = wp->w_grid.cols - 1 - virt_line_offset; + } } if (wlv.draw_state == WL_FOLD - 1 && wlv.n_extra == 0) { @@ -1714,6 +1739,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl wlv.draw_state = WL_STC; // Draw the 'statuscolumn' if option is set. if (statuscol.draw) { + if (sign_num.hl_id == 0) { + statuscol.num_attr = get_line_number_attr(wp, &wlv); + } if (statuscol.textp == NULL) { v = (ptr - line); get_statuscol_str(wp, lnum, wlv.row - startrow - wlv.filler_lines, &statuscol); @@ -1769,7 +1797,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl && wlv.vcol >= wp->w_virtcol) || (number_only && wlv.draw_state > WL_STC)) && wlv.filler_todo <= 0) { - draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row); + draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row); grid_put_linebuf(grid, wlv.row, 0, wlv.col, -grid->cols, wp->w_p_rl, wp, bg_attr, false); // Pretend we have finished updating the window. Except when // 'cursorcolumn' is set. @@ -2805,7 +2833,8 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl ? 1 : 0); if (has_decor) { - has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, wlv.col + eol_skip); + has_virttext = decor_redraw_eol(wp, &decor_state, &wlv.line_attr, + wlv.col + (wp->w_p_rl ? -eol_skip : eol_skip)); } if (((wp->w_p_cuc @@ -2893,9 +2922,10 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl } if (kv_size(fold_vt) > 0) { - draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, grid->cols, 0); + draw_virt_text_item(buf, win_col_offset, fold_vt, kHlModeCombine, + wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl); } - draw_virt_text(wp, buf, win_col_offset, &wlv.col, grid->cols, wlv.row); + draw_virt_text(wp, buf, win_col_offset, &wlv.col, wp->w_p_rl ? -1 : grid->cols, wlv.row); grid_put_linebuf(grid, wlv.row, 0, wlv.col, grid->cols, wp->w_p_rl, wp, bg_attr, false); wlv.row++; @@ -3159,9 +3189,9 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, bool number_onl int draw_col = wlv.col - wlv.boguscols; if (virt_line_offset >= 0) { draw_virt_text_item(buf, virt_line_offset, kv_A(virt_lines, virt_line_index).line, - kHlModeReplace, grid->cols, 0); + kHlModeReplace, wp->w_p_rl ? -1 : grid->cols, 0, wp->w_p_rl); } else if (wlv.filler_todo <= 0) { - draw_virt_text(wp, buf, win_col_offset, &draw_col, grid->cols, wlv.row); + draw_virt_text(wp, buf, win_col_offset, &draw_col, wp->w_p_rl ? -1 : grid->cols, wlv.row); } grid_put_linebuf(grid, wlv.row, 0, draw_col, grid->cols, wp->w_p_rl, wp, bg_attr, wrap); diff --git a/src/nvim/mouse.c b/src/nvim/mouse.c index e35385dd43..33d7bc2e51 100644 --- a/src/nvim/mouse.c +++ b/src/nvim/mouse.c @@ -1849,57 +1849,59 @@ 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); + // This may be -2 or -3 with 'foldcolumn' and empty line. + // In that case set it to -1 as it's just before start of line. + eol_vcol = MAX(eol_vcol, -1); + *vcolp = eol_vcol + (int)(off - off_r); + } else { + // Clicking on an empty line + *vcolp = click_col - start_col; } + } 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/options.lua b/src/nvim/options.lua index 2a1c76fd9a..e5821b62bd 100644 --- a/src/nvim/options.lua +++ b/src/nvim/options.lua @@ -4443,12 +4443,11 @@ return { deny_duplicates = true, desc = [=[ List of words that change the behavior of the |jumplist|. - stack Make the jumplist behave like the tagstack or like a - web browser. Relative location of entries in the - jumplist is preserved at the cost of discarding - subsequent entries when navigating backwards in the - jumplist and then jumping to a location. - |jumplist-stack| + stack Make the jumplist behave like the tagstack. + Relative location of entries in the jumplist is + preserved at the cost of discarding subsequent entries + when navigating backwards in the jumplist and then + jumping to a location. |jumplist-stack| view When moving through the jumplist, |changelist|, |alternate-file| or using |mark-motions| try to diff --git a/src/nvim/optionstr.c b/src/nvim/optionstr.c index 17afd10ee3..cf8e6cf641 100644 --- a/src/nvim/optionstr.c +++ b/src/nvim/optionstr.c @@ -151,13 +151,13 @@ void didset_string_options(void) (void)opt_strings_flags(p_vop, p_ssop_values, &vop_flags, true); (void)opt_strings_flags(p_fdo, p_fdo_values, &fdo_flags, true); (void)opt_strings_flags(p_dy, p_dy_values, &dy_flags, true); + (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); (void)opt_strings_flags(p_rdb, p_rdb_values, &rdb_flags, true); (void)opt_strings_flags(p_tc, p_tc_values, &tc_flags, false); (void)opt_strings_flags(p_tpf, p_tpf_values, &tpf_flags, true); (void)opt_strings_flags(p_ve, p_ve_values, &ve_flags, true); (void)opt_strings_flags(p_swb, p_swb_values, &swb_flags, true); (void)opt_strings_flags(p_wop, p_wop_values, &wop_flags, true); - (void)opt_strings_flags(p_jop, p_jop_values, &jop_flags, true); (void)opt_strings_flags(p_cb, p_cb_values, &cb_flags, true); } diff --git a/test/functional/ui/decorations_spec.lua b/test/functional/ui/decorations_spec.lua index daa4b4bdb3..d6a6243be2 100644 --- a/test/functional/ui/decorations_spec.lua +++ b/test/functional/ui/decorations_spec.lua @@ -1237,6 +1237,25 @@ describe('extmark decorations', function() | ]]} + meths.buf_set_extmark(0, ns, 4, 50, { virt_text={{'EOL', 'NonText'}} }) + screen:expect{grid=[[ + for _,item in ipairs(items) do | + local text, hl_id_cell, cou{4:Very} unpack(ite{4:VERY}| + if | + hl_id_cell ~= nil then {4:Much} {4:MUCH}| + --^ -- -- -- -- -- -- --{4:Error}- -- hl_i{4:ERROR}| + l_id_cell {1:EOL} | + end | + for _ = 1, (count or 1) do | + local cell = line[colpos] | + {1:-} cell.text = text {1:-}| + cell.hl_id = hl_id | + colpos = colpos+1 | + end | + end | + | + ]]} + feed '.' screen:expect{grid=[[ for _,item in ipairs(items) do | @@ -1244,7 +1263,7 @@ describe('extmark decorations', function() if | hl_id_cell ~= nil then {4:Much} {4:MUCH}| --^ -- -- -- -- -- -- -- -- -- -- -- hl_id | - = hl_id_cell {4:Error} {4:ERROR}| + = hl_id_cell {1:EOL} {4:Error} {4:ERROR}| end | for _ = 1, (count or 1) do | local cell = line[colpos] | @@ -1264,7 +1283,7 @@ describe('extmark decorations', function() {2: 3 } if | {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}| {2: 5 } --^ -- -- -- -- -- -- -- -- -- -- -- hl| - {2: }_id = hl_id_cell {4:Error} {4:ERROR}| + {2: }_id = hl_id_cell {1:EOL} {4:Error} {4:ERROR}| {2: 6 } end | {2: 7 } for _ = 1, (count or 1) do | {2: 8 } local cell = line[colpos] | @@ -1283,7 +1302,7 @@ describe('extmark decorations', function() {2: 3 } if | {2: 4 }hl_id_cell ~= nil then {4:Much} {4:MUCH}| {2: 5 } --^ -- -- -- -- -- -- -- -- -- -- -- hl| - _id = hl_id_cell {4:Error} {4:ERROR}| + _id = hl_id_cell {1:EOL} {4:Error} {4:ERROR}| {2: 6 } end | {2: 7 } for _ = 1, (count or 1) do | {2: 8 } local cell = line[colpos] | @@ -1891,6 +1910,84 @@ describe('extmark decorations', function() | ]]} end) + + it('virtual text works with rightleft', function() + screen:try_resize(50, 3) + insert('abcdefghijklmn') + feed('0') + command('set rightleft') + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'EOL', 'Underlined'}}}) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'right_align', 'Underlined'}}, virt_text_pos = 'right_align' }) + meths.buf_set_extmark(0, ns, 0, 0, { virt_text = {{'win_col', 'Underlined'}}, virt_text_win_col = 20 }) + meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'overlayed', 'Underlined'}}, virt_text_pos = 'overlay' }) + screen:expect{grid=[[ + {28:ngila_thgir} {28:loc_niw} {28:LOE} nml{28:deyalrevo}b^a| + {1: ~}| + | + ]]} + + insert(('#'):rep(32)) + feed('0') + screen:expect{grid=[[ + {28:ngila_tdeyalrevo}ba#####{28:loc_niw}###################^#| + {1: ~}| + | + ]]} + + insert(('#'):rep(16)) + feed('0') + screen:expect{grid=[[ + {28:ngila_thgir}############{28:loc_niw}###################^#| + {28:LOE} nml{28:deyalrevo}| + | + ]]} + + insert('###') + feed('0') + screen:expect{grid=[[ + #################################################^#| + {28:ngila_thgir} {28:loc_niw} {28:LOE} nml{28:deyalrevo}ba#| + | + ]]} + + command('set number') + screen:expect{grid=[[ + #############################################^#{2: 1 }| + {28:ngila_thgir} {28:loc_niw} nml{28:deyalrevo}ba#####{2: }| + | + ]]} + + command('set cpoptions+=n') + screen:expect{grid=[[ + #############################################^#{2: 1 }| + {28:ngila_thgir} {28:loc_niw} nml{28:deyalrevo}ba#####| + | + ]]} + end) + + it('works with double width char and rightleft', function() + screen:try_resize(50, 3) + insert('abcdefghij口klmnop') + feed('0') + command('set rightleft') + screen:expect{grid=[[ + ponmlk口jihgfedcb^a| + {1: ~}| + | + ]]} + meths.buf_set_extmark(0, ns, 0, 2, { virt_text = {{'overlayed', 'Underlined'}}, virt_text_pos = 'overlay' }) + screen:expect{grid=[[ + ponmlk {28:deyalrevo}b^a| + {1: ~}| + | + ]]} + meths.buf_set_extmark(0, ns, 0, 15, { virt_text = {{'古', 'Underlined'}}, virt_text_pos = 'overlay' }) + screen:expect{grid=[[ + po{28:古}lk {28:deyalrevo}b^a| + {1: ~}| + | + ]]} + end) end) describe('decorations: inline virtual text', function() @@ -4226,6 +4323,7 @@ if (h->n_buckets < new_n_buckets) { // expand end) it('does not show twice if end_row or end_col is specified #18622', function() + screen:try_resize(50, 8) insert([[ aaa bbb @@ -4241,10 +4339,28 @@ if (h->n_buckets < new_n_buckets) { // expand dd^d | {1:VIRT LINE 2} | {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| - {1:~ }| + | + ]]} + end) + + it('works with rightleft', function() + screen:try_resize(50, 8) + insert([[ + aaa + bbb + ccc + ddd]]) + command('set number rightleft') + meths.buf_set_extmark(0, ns, 0, 0, {virt_lines = {{{'VIRT LINE 1', 'NonText'}}}, virt_lines_leftcol = true}) + meths.buf_set_extmark(0, ns, 3, 0, {virt_lines = {{{'VIRT LINE 2', 'NonText'}}}}) + screen:expect{grid=[[ + aaa{9: 1 }| + {1:1 ENIL TRIV}| + bbb{9: 2 }| + ccc{9: 3 }| + ^ddd{9: 4 }| + {1:2 ENIL TRIV}{9: }| + {1: ~}| | ]]} end) diff --git a/test/functional/ui/float_spec.lua b/test/functional/ui/float_spec.lua index f75fb52108..4fea513249 100644 --- a/test/functional/ui/float_spec.lua +++ b/test/functional/ui/float_spec.lua @@ -6287,7 +6287,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 +6366,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 +8764,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 c8ca5be282..5e907f82ba 100644 --- a/test/functional/ui/fold_spec.lua +++ b/test/functional/ui/fold_spec.lua @@ -3047,6 +3047,39 @@ describe("folded lines", function() {11:-- VISUAL LINE --} | ]]) end + + meths.set_option_value('rightleft', true, {}) + if multigrid then + screen:expect([[ + ## grid 1 + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [2:------------------------------]| + [3:------------------------------]| + ## 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: +│}| + {1: ~}| + {1: ~}| + ## grid 3 + {11:-- VISUAL LINE --} | + ]]) + else + 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: +│}| + {1: ~}| + {1: ~}| + {11:-- VISUAL LINE --} | + ]]) + end end) end 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/functional/ui/statuscolumn_spec.lua b/test/functional/ui/statuscolumn_spec.lua index 742976cbe2..a7830abe8f 100644 --- a/test/functional/ui/statuscolumn_spec.lua +++ b/test/functional/ui/statuscolumn_spec.lua @@ -475,7 +475,7 @@ describe('statuscolumn', function() aaaaaaaaa | {1:virtual-2 15}virt_line1 | {1:virtual-2 15}virt_line2 | - {1:buffer 0 16}{5:^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| + {4:buffer 0 16}{5:^aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa}| {5:aaaaaaaaa }| {1:virtual-1 16}END | {0:~ }| diff --git a/test/old/testdir/test_filetype.vim b/test/old/testdir/test_filetype.vim index e2b977c2b8..e5d7085d71 100644 --- a/test/old/testdir/test_filetype.vim +++ b/test/old/testdir/test_filetype.vim @@ -97,6 +97,7 @@ func s:GetFilenameChecks() abort \ 'asterisk': ['asterisk/file.conf', 'asterisk/file.conf-file', 'some-asterisk/file.conf', 'some-asterisk/file.conf-file'], \ 'astro': ['file.astro'], \ 'atlas': ['file.atl', 'file.as'], + \ 'authzed': ['file.zed'], \ 'autohotkey': ['file.ahk'], \ 'autoit': ['file.au3'], \ 'automake': ['GNUmakefile.am', 'makefile.am', 'Makefile.am'], diff --git a/test/old/testdir/test_jumplist.vim b/test/old/testdir/test_jumplist.vim index 91ad940e18..b4dcdad9d6 100644 --- a/test/old/testdir/test_jumplist.vim +++ b/test/old/testdir/test_jumplist.vim @@ -104,4 +104,70 @@ d bwipe! endfunc +" Test for 'jumpoptions' +func Test_jumpoptions() + new + call setline(1, range(1, 200)) + clearjumps + set jumpoptions=stack + + " Jump around to add some locations to the jump list. + normal 10G + normal 20G + normal 30G + normal 40G + normal 50G + let bnr = bufnr() + + " discards the tail when navigating from the middle + exe "normal \<C-O>\<C-O>" + call assert_equal([ + \ [{'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 10, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 20, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 30, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 40, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 50, 'bufnr': bnr, 'col': 0, 'coladd': 0} + \ ], 3], getjumplist()) + + " new jump location is added immediately after the last one + normal 90G + call assert_equal([ + \ [{'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 10, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 20, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 30, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ ], 4], getjumplist()) + + " does not add the same location twice adjacently + normal 60G + normal 60G + call assert_equal([ + \ [{'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 10, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 20, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 30, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 90, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + "\ Nvim: avoids useless/phantom jumps + "\ {'lnum': 60, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + "\ ], 6], getjumplist()) + \ ], 5], getjumplist()) + + " does add the same location twice non adjacently + normal 10G + normal 20G + call assert_equal([ + \ [{'lnum': 1, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 10, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 20, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 30, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 90, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 60, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ {'lnum': 10, 'bufnr': bnr, 'col': 0, 'coladd': 0}, + \ ], 7], getjumplist()) + + set jumpoptions& + %bw! +endfunc + " vim: shiftwidth=2 sts=2 expandtab 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..4c11fcd99e 100644 --- a/test/old/testdir/test_virtualedit.vim +++ b/test/old/testdir/test_virtualedit.vim @@ -564,35 +564,38 @@ 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()) |