aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--runtime/autoload/health/provider.vim28
-rw-r--r--runtime/autoload/provider/node.vim18
-rw-r--r--runtime/doc/builtin.txt24
-rw-r--r--runtime/doc/eval.txt5
-rw-r--r--runtime/doc/news.txt4
-rw-r--r--runtime/lua/man.lua54
-rw-r--r--runtime/lua/vim/lsp/semantic_tokens.lua2
-rwxr-xr-xsrc/nvim/CMakeLists.txt13
-rw-r--r--src/nvim/eval.c2
-rw-r--r--src/nvim/eval.h1
-rw-r--r--test/functional/plugin/man_spec.lua35
-rw-r--r--test/old/testdir/test_cursor_func.vim12
-rw-r--r--test/old/testdir/test_normal.vim8
-rw-r--r--test/old/testdir/test_put.vim2
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