diff options
-rw-r--r-- | runtime/autoload/health/provider.vim | 28 | ||||
-rw-r--r-- | runtime/autoload/provider/node.vim | 18 | ||||
-rw-r--r-- | runtime/doc/builtin.txt | 24 | ||||
-rw-r--r-- | runtime/doc/eval.txt | 5 | ||||
-rw-r--r-- | runtime/doc/news.txt | 4 | ||||
-rw-r--r-- | runtime/lua/man.lua | 54 | ||||
-rw-r--r-- | runtime/lua/vim/lsp/semantic_tokens.lua | 2 | ||||
-rwxr-xr-x | src/nvim/CMakeLists.txt | 13 | ||||
-rw-r--r-- | src/nvim/eval.c | 2 | ||||
-rw-r--r-- | src/nvim/eval.h | 1 | ||||
-rw-r--r-- | test/functional/plugin/man_spec.lua | 35 | ||||
-rw-r--r-- | test/old/testdir/test_cursor_func.vim | 12 | ||||
-rw-r--r-- | test/old/testdir/test_normal.vim | 8 | ||||
-rw-r--r-- | test/old/testdir/test_put.vim | 2 |
14 files changed, 131 insertions, 77 deletions
diff --git a/runtime/autoload/health/provider.vim b/runtime/autoload/health/provider.vim index d104bcfd67..59361eb735 100644 --- a/runtime/autoload/health/provider.vim +++ b/runtime/autoload/health/provider.vim @@ -19,22 +19,6 @@ function! s:cmd_ok(cmd) abort return v:shell_error == 0 endfunction -" Simple version comparison. -function! s:version_cmp(a, b) abort - let a = split(a:a, '\.', 0) - let b = split(a:b, '\.', 0) - - for i in range(len(a)) - if str2nr(a[i]) > str2nr(b[i]) - return 1 - elseif str2nr(a[i]) < str2nr(b[i]) - return -1 - endif - endfor - - return 0 -endfunction - " Handler for s:system() function. function! s:system_handler(jobid, data, event) dict abort if a:event ==# 'stderr' @@ -244,7 +228,7 @@ function! s:version_info(python) abort let nvim_path_base = fnamemodify(nvim_path, ':~:h') let version_status = 'unknown; '.nvim_path_base if !s:is_bad_response(nvim_version) && !s:is_bad_response(pypi_version) - if s:version_cmp(nvim_version, pypi_version) == -1 + if v:lua.vim.version.lt(nvim_version, pypi_version) let version_status = 'outdated; from '.nvim_path_base else let version_status = 'up to date' @@ -598,7 +582,7 @@ function! s:check_ruby() abort return endif - if s:version_cmp(current_gem, latest_gem) == -1 + if v:lua.vim.version.lt(current_gem, latest_gem) call health#report_warn( \ printf('Gem "neovim" is out-of-date. Installed: %s, latest: %s', \ current_gem, latest_gem), @@ -623,8 +607,8 @@ function! s:check_node() abort endif let node_v = get(split(s:system(['node', '-v']), "\n"), 0, '') call health#report_info('Node.js: '. node_v) - if s:shell_error || s:version_cmp(node_v[1:], '6.0.0') < 0 - call health#report_warn('Nvim node.js host does not support '.node_v) + if s:shell_error || v:lua.vim.version.lt(node_v[1:], '6.0.0') + call health#report_warn('Nvim node.js host does not support Node '.node_v) " Skip further checks, they are nonsense if nodejs is too old. return endif @@ -675,7 +659,7 @@ function! s:check_node() abort return endif - if s:version_cmp(current_npm, latest_npm) == -1 + if latest_npm !=# 'unable to parse' && v:lua.vim.version.lt(current_npm, latest_npm) call health#report_warn( \ printf('Package "neovim" is out-of-date. Installed: %s, latest: %s', \ current_npm, latest_npm), @@ -751,7 +735,7 @@ function! s:check_perl() abort return endif - if s:version_cmp(current_cpan, latest_cpan) == -1 + if v:lua.vim.version.lt(current_cpan, latest_cpan) call health#report_warn( \ printf('Module "Neovim::Ext" is out-of-date. Installed: %s, latest: %s', \ current_cpan, latest_cpan), diff --git a/runtime/autoload/provider/node.vim b/runtime/autoload/provider/node.vim index 87af0094fe..3e7b8b4ef9 100644 --- a/runtime/autoload/provider/node.vim +++ b/runtime/autoload/provider/node.vim @@ -3,7 +3,7 @@ if exists('g:loaded_node_provider') endif let g:loaded_node_provider = 1 -function! s:is_minimum_version(version, min_major, min_minor) abort +function! s:is_minimum_version(version, min_version) abort if empty(a:version) let nodejs_version = get(split(system(['node', '-v']), "\n"), 0, '') if v:shell_error || nodejs_version[0] !=# 'v' @@ -15,11 +15,7 @@ function! s:is_minimum_version(version, min_major, min_minor) abort " Remove surrounding junk. Example: 'v4.12.0' => '4.12.0' let nodejs_version = matchstr(nodejs_version, '\(\d\.\?\)\+') " [major, minor, patch] - let v_list = split(nodejs_version, '\.') - return len(v_list) == 3 - \ && ((str2nr(v_list[0]) > str2nr(a:min_major)) - \ || (str2nr(v_list[0]) == str2nr(a:min_major) - \ && str2nr(v_list[1]) >= str2nr(a:min_minor))) + return !v:lua.vim.version.lt(nodejs_version, a:min_version) endfunction let s:NodeHandler = { @@ -43,20 +39,20 @@ function! provider#node#can_inspect() abort if v:shell_error || ver[0] !=# 'v' return 0 endif - return (ver[1] ==# '6' && s:is_minimum_version(ver, 6, 12)) - \ || s:is_minimum_version(ver, 7, 6) + return (ver[1] ==# '6' && s:is_minimum_version(ver, '6.12.0')) + \ || s:is_minimum_version(ver, '7.6.0') endfunction function! provider#node#Detect() abort - let minver = [6, 0] + let minver = '6.0.0' if exists('g:node_host_prog') return [expand(g:node_host_prog, v:true), ''] endif if !executable('node') return ['', 'node not found (or not executable)'] endif - if !s:is_minimum_version(v:null, minver[0], minver[1]) - return ['', printf('node version %s.%s not found', minver[0], minver[1])] + if !s:is_minimum_version(v:null, minver) + return ['', printf('node version %s not found', minver)] endif let npm_opts = {} diff --git a/runtime/doc/builtin.txt b/runtime/doc/builtin.txt index 7fab2ac6ff..b0b7809e8c 100644 --- a/runtime/doc/builtin.txt +++ b/runtime/doc/builtin.txt @@ -2890,6 +2890,9 @@ getcharpos({expr}) Get the position for String {expr}. Same as |getpos()| but the column number in the returned List is a character index instead of a byte index. + If |getpos()| returns a very large column number, equal to + |v:maxcol|, then getcharpos() will return the character index + of the last character. Example: With the cursor on '세' in line 5 with text "여보세요": > @@ -3064,10 +3067,11 @@ getcompletion({pat}, {type} [, {filtered}]) *getcompletion()* *getcurpos()* getcurpos([{winid}]) Get the position of the cursor. This is like getpos('.'), but - includes an extra "curswant" in the list: + includes an extra "curswant" item in the list: [0, lnum, col, off, curswant] ~ The "curswant" number is the preferred column when moving the - cursor vertically. Also see |getcursorcharpos()| and + cursor vertically. After |$| command it will be a very large + number equal to |v:maxcol|. Also see |getcursorcharpos()| and |getpos()|. The first "bufnum" item is always zero. The byte position of the cursor is returned in "col". To get the character @@ -3389,12 +3393,12 @@ getpos({expr}) Get the position for String {expr}. For possible values of character. Note that for '< and '> Visual mode matters: when it is "V" (visual line mode) the column of '< is zero and the column of - '> is a large number. + '> is a large number equal to |v:maxcol|. The column number in the returned List is the byte position within the line. To get the character position in the line, use |getcharpos()|. - The column number can be very large, e.g. 2147483647, in which - case it means "after the end of the line". + A very large column number equal to |v:maxcol| can be returned, + in which case it means "after the end of the line". If {expr} is invalid, returns a list with all zeros. This can be used to save and restore the position of a mark: > let save_a_mark = getpos("'a") @@ -6570,6 +6574,8 @@ screenpos({winid}, {lnum}, {col}) *screenpos()* as if 'conceallevel' is zero. You can set the cursor to the right position and use |screencol()| to get the value with |conceal| taken into account. + If the position is in a closed fold the screen position of the + first character is returned, {col} is not used. Returns an empty Dict if {winid} is invalid. Can also be used as a |method|: > @@ -9407,10 +9413,14 @@ winsaveview() Returns a |Dictionary| that contains information to restore The return value includes: lnum cursor line number col cursor column (Note: the first column - zero, as opposed to what getpos() + zero, as opposed to what |getcurpos()| returns) coladd cursor column offset for 'virtualedit' - curswant column for vertical movement + curswant column for vertical movement (Note: + the first column is zero, as opposed + to what |getcurpos()| returns). After + |$| command it will be a very large + number equal to |v:maxcol|. topline first line in the window topfill filler lines, only in diff mode leftcol first column displayed; only used when diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt index d79b446986..fe15ba6115 100644 --- a/runtime/doc/eval.txt +++ b/runtime/doc/eval.txt @@ -1979,6 +1979,11 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr', v:lua Prefix for calling Lua functions from expressions. See |v:lua-call| for more information. + *v:maxcol* *maxcol-variable* +v:maxcol Maximum line length. Depending on where it is used it can be + screen columns, characters or bytes. The value currently is + 2147483647 on all systems. + *v:mouse_win* *mouse_win-variable* v:mouse_win Window number for a mouse click obtained with |getchar()|. First window has number 1, like with |winnr()|. The value is diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index 2db1e75bf7..f5b9f39d93 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -45,7 +45,7 @@ The following changes may require adaptations in user config or plugins. - `printheader` - `printmbcharset` -• libiconv is now a required build dependency. +• libiconv and intl are now required build dependencies. • Unsaved changes are now preserved rather than discarded when |channel-stdio| is closed. @@ -162,6 +162,8 @@ The following new APIs or features were added. • |:highlight| now supports an additional attribute "altfont". +• |:Man| manpage viewer supports manpage names containing spaces. + • Treesitter captures can now be transformed by directives. This will allow more complicated dynamic language injections. diff --git a/runtime/lua/man.lua b/runtime/lua/man.lua index 61a9575312..cca9434e9c 100644 --- a/runtime/lua/man.lua +++ b/runtime/lua/man.lua @@ -328,7 +328,8 @@ local function get_path(sect, name, silent) -- find any that match the specified name ---@param v string local namematches = vim.tbl_filter(function(v) - return fn.fnamemodify(v, ':t'):match(name) + local tail = fn.fnamemodify(v, ':t') + return string.find(tail, name, 1, true) end, results) or {} local sectmatches = {} @@ -372,18 +373,18 @@ local function extract_sect_and_name_ref(ref) if not name then man_error('manpage reference cannot contain only parentheses: ' .. ref) end - return '', spaces_to_underscores(name) + return '', name end local parts = vim.split(ref1, '(', { plain = true }) -- see ':Man 3X curses' on why tolower. -- TODO(nhooyr) Not sure if this is portable across OSs -- but I have not seen a single uppercase section. local sect = vim.split(parts[2] or '', ')', { plain = true })[1]:lower() - local name = spaces_to_underscores(parts[1]) + local name = parts[1] return sect, name end --- verify_exists attempts to find the path to a manpage +-- find_path attempts to find the path to a manpage -- based on the passed section and name. -- -- 1. If manpage could not be found with the given sect and name, @@ -391,10 +392,10 @@ end -- 2. If it still could not be found, then we try again without a section. -- 3. If still not found but $MANSECT is set, then we try again with $MANSECT -- unset. +-- 4. If a path still wasn't found, return nil. ---@param sect string? ---@param name string ----@param silent boolean? -local function verify_exists(sect, name, silent) +function M.find_path(sect, name) if sect and sect ~= '' then local ret = get_path(sect, name, true) if ret then @@ -430,10 +431,8 @@ local function verify_exists(sect, name, silent) end end - if not silent then - -- finally, if that didn't work, there is no hope - man_error('no manual entry for ' .. name) - end + -- finally, if that didn't work, there is no hope + return nil end local EXT_RE = vim.regex([[\.\%([glx]z\|bz2\|lzma\|Z\)$]]) @@ -585,8 +584,8 @@ local function get_paths(sect, name) ---@type string[] local paths = fn.globpath(mandirs, 'man?/' .. name .. '*.' .. sect .. '*', false, true) - -- Prioritize the result from verify_exists as it obeys b:man_default_sects. - local first = verify_exists(sect, name, true) + -- Prioritize the result from find_path as it obeys b:man_default_sects. + local first = M.find_path(sect, name) if first then paths = move_elem_to_head(paths, first) end @@ -728,10 +727,6 @@ end ---@param count integer ---@param args string[] function M.open_page(count, smods, args) - if #args > 2 then - man_error('too many arguments') - end - local ref ---@type string if #args == 0 then ref = vim.bo.filetype == 'man' and fn.expand('<cWORD>') or fn.expand('<cword>') @@ -743,9 +738,14 @@ function M.open_page(count, smods, args) else -- Combine the name and sect into a manpage reference so that all -- verification/extraction can be kept in a single function. - -- If args[2] is a reference as well, that is fine because it is the only - -- reference that will match. - ref = ('%s(%s)'):format(args[2], args[1]) + if tonumber(args[1]) then + local sect = args[1] + table.remove(args, 1) + local name = table.concat(args, ' ') + ref = ('%s(%s)'):format(name, sect) + else + ref = table.concat(args, ' ') + end end local sect, name = extract_sect_and_name_ref(ref) @@ -753,9 +753,16 @@ function M.open_page(count, smods, args) sect = tostring(count) end - local path = verify_exists(sect, name) - sect, name = extract_sect_and_name_path(path) + -- Try both spaces and underscores, use the first that exists. + local path = M.find_path(sect, name) + if path == nil then + path = M.find_path(sect, spaces_to_underscores(name)) + if path == nil then + man_error('no manual entry for ' .. name) + end + end + sect, name = extract_sect_and_name_path(path) local buf = fn.bufnr() local save_tfu = vim.bo[buf].tagfunc vim.bo[buf].tagfunc = "v:lua.require'man'.goto_tag" @@ -786,7 +793,10 @@ end -- Called when a man:// buffer is opened. function M.read_page(ref) local sect, name = extract_sect_and_name_ref(ref) - local path = verify_exists(sect, name) + local path = M.find_path(sect, name) + if path == nil then + man_error('no manual entry for ' .. name) + end sect = extract_sect_and_name_path(path) local page = get_page(path) vim.b.man_sect = sect diff --git a/runtime/lua/vim/lsp/semantic_tokens.lua b/runtime/lua/vim/lsp/semantic_tokens.lua index 9eaccd539f..a5e007a011 100644 --- a/runtime/lua/vim/lsp/semantic_tokens.lua +++ b/runtime/lua/vim/lsp/semantic_tokens.lua @@ -435,7 +435,7 @@ function STHighlighter:on_win(topline, botline) token.marked = true api.nvim_exec_autocmds('LspTokenUpdate', { - pattern = vim.api.nvim_buf_get_name(self.bufnr), + buffer = self.bufnr, modeline = false, data = { token = token, diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt index f3344c10de..b02c740471 100755 --- a/src/nvim/CMakeLists.txt +++ b/src/nvim/CMakeLists.txt @@ -32,14 +32,11 @@ target_link_libraries(main_lib INTERFACE treesitter unibilium) -option(ENABLE_LIBINTL "enable libintl" ON) -if(ENABLE_LIBINTL) - # Libintl (not Intl) selects our FindLibintl.cmake script. #8464 - find_package(Libintl REQUIRED) - target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBINTL_INCLUDE_DIR}) - if (LIBINTL_LIBRARY) - target_link_libraries(main_lib INTERFACE ${LIBINTL_LIBRARY}) - endif() +# Libintl (not Intl) selects our FindLibintl.cmake script. #8464 +find_package(Libintl REQUIRED) +target_include_directories(main_lib SYSTEM BEFORE INTERFACE ${LIBINTL_INCLUDE_DIR}) +if (LIBINTL_LIBRARY) + target_link_libraries(main_lib INTERFACE ${LIBINTL_LIBRARY}) endif() # The unit test lib requires LuaJIT; it will be skipped if LuaJIT is missing. diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e10607772a..cbad5d04ff 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -251,6 +251,7 @@ static struct vimvar { VV(VV_ARGV, "argv", VAR_LIST, VV_RO), VV(VV_COLLATE, "collate", VAR_STRING, VV_RO), VV(VV_EXITING, "exiting", VAR_NUMBER, VV_RO), + VV(VV_MAXCOL, "maxcol", VAR_NUMBER, VV_RO), // Neovim VV(VV_STDERR, "stderr", VAR_NUMBER, VV_RO), VV(VV_MSGPACK_TYPES, "msgpack_types", VAR_DICT, VV_RO), @@ -451,6 +452,7 @@ void eval_init(void) set_vim_var_nr(VV_NUMBERMIN, VARNUMBER_MIN); set_vim_var_nr(VV_NUMBERSIZE, sizeof(varnumber_T) * 8); set_vim_var_special(VV_EXITING, kSpecialVarNull); + set_vim_var_nr(VV_MAXCOL, MAXCOL); set_vim_var_nr(VV_ECHOSPACE, sc_col - 1); diff --git a/src/nvim/eval.h b/src/nvim/eval.h index 86bc76e793..aa034cb2b3 100644 --- a/src/nvim/eval.h +++ b/src/nvim/eval.h @@ -157,6 +157,7 @@ typedef enum { VV_ARGV, VV_COLLATE, VV_EXITING, + VV_MAXCOL, // Nvim VV_STDERR, VV_MSGPACK_TYPES, diff --git a/test/functional/plugin/man_spec.lua b/test/functional/plugin/man_spec.lua index 58da059be6..9730bf4bf6 100644 --- a/test/functional/plugin/man_spec.lua +++ b/test/functional/plugin/man_spec.lua @@ -8,9 +8,29 @@ local nvim_prog = helpers.nvim_prog local matches = helpers.matches local write_file = helpers.write_file local tmpname = helpers.tmpname +local eq = helpers.eq local skip = helpers.skip local is_ci = helpers.is_ci +-- Collects all names passed to find_path() after attempting ":Man foo". +local function get_search_history(name) + local args = vim.split(name, ' ') + local code = [[ + local args = ... + local man = require('runtime.lua.man') + local res = {} + man.find_path = function(sect, name) + table.insert(res, name) + return nil + end + local ok, rv = pcall(man.open_page, 0, {tab = 0}, args) + assert(not ok) + assert(rv and rv:match('no manual entry')) + return res + ]] + return exec_lua(code, args) +end + clear() if funcs.executable('man') == 0 then pending('missing "man" command', function() end) @@ -173,4 +193,19 @@ describe(':Man', function() funcs.system(args, {''})) os.remove(actual_file) end) + + it('tries variants with spaces, underscores #22503', function() + eq({ + 'NAME WITH SPACES', + 'NAME_WITH_SPACES', + }, get_search_history('NAME WITH SPACES')) + eq({ + 'some other man', + 'some_other_man', + }, get_search_history('3 some other man')) + eq({ + 'other_man', + 'other_man', + }, get_search_history('other_man(1)')) + end) end) diff --git a/test/old/testdir/test_cursor_func.vim b/test/old/testdir/test_cursor_func.vim index bb8e7cd5c5..239eff5db5 100644 --- a/test/old/testdir/test_cursor_func.vim +++ b/test/old/testdir/test_cursor_func.vim @@ -40,6 +40,18 @@ func Test_move_cursor() quit! endfunc +func Test_curswant_maxcol() + new + call setline(1, 'foo') + + " Test that after "$" command curswant is set to the same value as v:maxcol. + normal! 1G$ + call assert_equal(v:maxcol, getcurpos()[4]) + call assert_equal(v:maxcol, winsaveview().curswant) + + quit! +endfunc + " Very short version of what matchparen does. function s:Highlight_Matching_Pair() let save_cursor = getcurpos() diff --git a/test/old/testdir/test_normal.vim b/test/old/testdir/test_normal.vim index 48e6bc5298..ca91ab2ceb 100644 --- a/test/old/testdir/test_normal.vim +++ b/test/old/testdir/test_normal.vim @@ -921,7 +921,7 @@ func Test_normal14_page() set nostartofline exe "norm! $\<c-b>" call assert_equal('92', getline('.')) - call assert_equal([0, 92, 2, 0, 2147483647], getcurpos()) + call assert_equal([0, 92, 2, 0, v:maxcol], getcurpos()) " cleanup set startofline bw! @@ -966,7 +966,7 @@ func Test_normal15_z_scroll_vert() norm! >>$ztzb call assert_equal(' 30', getline('.')) call assert_equal(30, winsaveview()['topline']+winheight(0)-1) - call assert_equal([0, 30, 3, 0, 2147483647], getcurpos()) + call assert_equal([0, 30, 3, 0, v:maxcol], getcurpos()) " Test for z- 1 @@ -2917,7 +2917,7 @@ func Test_normal36_g_cmd5() call assert_equal([0, 14, 1, 0, 1], getcurpos()) " count > buffer content norm! 120go - call assert_equal([0, 14, 1, 0, 2147483647], getcurpos()) + call assert_equal([0, 14, 1, 0, v:maxcol], getcurpos()) " clean up bw! endfunc @@ -3097,7 +3097,7 @@ func Test_normal42_halfpage() set nostartofline exe "norm! $\<c-u>" call assert_equal('95', getline('.')) - call assert_equal([0, 95, 2, 0, 2147483647], getcurpos()) + call assert_equal([0, 95, 2, 0, v:maxcol], getcurpos()) " cleanup set startofline bw! diff --git a/test/old/testdir/test_put.vim b/test/old/testdir/test_put.vim index 25fc5a2f04..6c7cfa09c6 100644 --- a/test/old/testdir/test_put.vim +++ b/test/old/testdir/test_put.vim @@ -209,7 +209,7 @@ func Test_multibyte_op_end_mark() call assert_equal([0, 1, 7, 0], getpos("']")) normal Vyp - call assert_equal([0, 1, 2147483647, 0], getpos("'>")) + call assert_equal([0, 1, v:maxcol, 0], getpos("'>")) call assert_equal([0, 2, 7, 0], getpos("']")) bwipe! endfunc |