Merge remote-tracking branch 'origin/master' into lsp-followup

This commit is contained in:
Ashkan Kiani
2019-11-21 10:04:32 -08:00
35 changed files with 1993 additions and 1494 deletions

1
.gitignore vendored
View File

@@ -10,6 +10,7 @@ compile_commands.json
/dist/
/.deps/
/tmp/
/.clangd/
*.mo
.*.sw?

View File

@@ -114,6 +114,11 @@ if (-Not (Test-Path -PathType Leaf "$env:TREE_SITTER_DIR\bin\c.dll")) {
exit 1
}
if ($compiler -eq 'MSVC') {
# Required for LuaRocks (https://github.com/luarocks/luarocks/issues/1039#issuecomment-507296940).
$env:VCINSTALLDIR = "C:/Program Files (x86)/Microsoft Visual Studio/2017/Community/VC/Tools/MSVC/14.16.27023/"
}
function convertToCmakeArgs($vars) {
return $vars.GetEnumerator() | foreach { "-D$($_.Key)=$($_.Value)" }
}

View File

@@ -64,33 +64,20 @@ function! man#open_page(count, count1, mods, ...) abort
return
endtry
call s:push_tag()
let bufname = 'man://'.name.(empty(sect)?'':'('.sect.')')
let [l:buf, l:save_tfu] = [bufnr(), &tagfunc]
try
set eventignore+=BufReadCmd
set tagfunc=man#goto_tag
let l:target = l:name . '(' . l:sect . ')'
if a:mods !~# 'tab' && s:find_man()
execute 'silent keepalt edit' fnameescape(bufname)
execute 'silent keepalt tag' l:target
else
execute 'silent keepalt' a:mods 'split' fnameescape(bufname)
execute 'silent keepalt' a:mods 'stag' l:target
endif
finally
set eventignore-=BufReadCmd
endtry
try
let page = s:get_page(path)
catch
if a:mods =~# 'tab' || !s:find_man()
" a new window was opened
close
endif
call s:error(v:exception)
return
call setbufvar(l:buf, '&tagfunc', l:save_tfu)
endtry
let b:man_sect = sect
call s:put_page(page)
endfunction
function! man#read_page(ref) abort
@@ -163,6 +150,7 @@ endfunction
function! s:put_page(page) abort
setlocal modifiable
setlocal noreadonly
setlocal noswapfile
silent keepjumps %delete _
silent put =a:page
while getline(1) =~# '^\s*$'
@@ -254,24 +242,6 @@ function! s:verify_exists(sect, name) abort
return s:extract_sect_and_name_path(path) + [path]
endfunction
let s:tag_stack = []
function! s:push_tag() abort
let s:tag_stack += [{
\ 'buf': bufnr('%'),
\ 'lnum': line('.'),
\ 'col': col('.'),
\ }]
endfunction
function! man#pop_tag() abort
if !empty(s:tag_stack)
let tag = remove(s:tag_stack, -1)
execute 'silent' tag['buf'].'buffer'
call cursor(tag['lnum'], tag['col'])
endif
endfunction
" extracts the name and sect out of 'path/name.sect'
function! s:extract_sect_and_name_path(path) abort
let tail = fnamemodify(a:path, ':t')
@@ -356,14 +326,18 @@ function! man#complete(arg_lead, cmd_line, cursor_pos) abort
return s:complete(sect, sect, name)
endfunction
function! s:complete(sect, psect, name) abort
function! s:get_paths(sect, name) abort
try
let mandirs = join(split(s:system(['man', s:find_arg]), ':\|\n'), ',')
catch
call s:error(v:exception)
return
endtry
let pages = globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1)
return globpath(mandirs,'man?/'.a:name.'*.'.a:sect.'*', 0, 1)
endfunction
function! s:complete(sect, psect, name) abort
let pages = s:get_paths(a:sect, a:name)
" We remove duplicates in case the same manpage in different languages was found.
return uniq(sort(map(pages, 's:format_candidate(v:val, a:psect)'), 'i'))
endfunction
@@ -402,4 +376,27 @@ function! man#init_pager() abort
endif
endfunction
function! man#goto_tag(pattern, flags, info) abort
let [l:sect, l:name] = man#extract_sect_and_name_ref(a:pattern)
let l:paths = s:get_paths(l:sect, l:name)
let l:structured = []
for l:path in l:paths
let l:n = s:extract_sect_and_name_path(l:path)[1]
let l:structured += [{ 'name': l:n, 'path': l:path }]
endfor
" sort by relevance - exact matches first, then the previous order
call sort(l:structured, { a, b -> a.name ==? l:name ? -1 : b.name ==? l:name ? 1 : 0 })
return map(l:structured, {
\ _, entry -> {
\ 'name': entry.name,
\ 'filename': 'man://' . entry.path,
\ 'cmd': '1'
\ }
\ })
endfunction
call s:init()

View File

@@ -462,7 +462,7 @@ We can get a mark by its id: >
We can get all marks in a buffer for our namespace (or by a range): >
echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, -1)
echo nvim_buf_get_extmarks(0, g:mark_ns, 0, -1, {})
=> [[1, 0, 2]]
Deleting all text surrounding an extmark does not remove the extmark. To
@@ -1516,6 +1516,13 @@ nvim_buf_attach({buffer}, {send_buffer}, {opts}) *nvim_buf_attach()*
Activates buffer-update events on a channel, or as Lua
callbacks.
Example (Lua): capture buffer updates in a global `events` variable (use "print(vim.inspect(events))" to see its
contents): >
events = {}
vim.api.nvim_buf_attach(0, false, {
on_lines=function(...) table.insert(events, {...}) end})
<
Parameters: ~
{buffer} Buffer handle, or 0 for current buffer
{send_buffer} True if the initial notification should
@@ -1804,21 +1811,22 @@ nvim_buf_get_extmarks({buffer}, {ns_id}, {start}, {end}, {opts})
range ends can be specified as (row, col) tuples, as well as
extmark ids in the same namespace. In addition, 0 and -1 works
as shorthands for (0,0) and (-1,-1) respectively, so that all
marks in the buffer can be quieried as:
marks in the buffer can be queried as:
all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1)
all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
If end is a lower position than start, then the range will be
traversed backwards. This is mostly used with limited amount,
to be able to get the first marks prior to a given position.
traversed backwards. This is mostly useful with limited
amount, to be able to get the first marks prior to a given
position.
Parameters: ~
{buffer} The buffer handle
{ns_id} An id returned previously from
nvim_create_namespace
{lower} One of: extmark id, (row, col) or 0, -1 for
{start} One of: extmark id, (row, col) or 0, -1 for
buffer ends
{upper} One of: extmark id, (row, col) or 0, -1 for
{end} One of: extmark id, (row, col) or 0, -1 for
buffer ends
{opts} additional options. Supports the keys:
• amount: Maximum number of marks to return
@@ -1845,7 +1853,7 @@ nvim_buf_set_extmark({buffer}, {ns_id}, {id}, {line}, {col}, {opts})
{ns_id} a identifier returned previously with
nvim_create_namespace
{id} The extmark's id or 0 to create a new mark.
{row} The row to set the extmark to.
{line} The row to set the extmark to.
{col} The column to set the extmark to.
{opts} Optional parameters. Currently not used.

View File

@@ -1738,7 +1738,7 @@ v:lnum Line number for the 'foldexpr' |fold-expr|, 'formatexpr' and
|sandbox|.
*v:lua* *lua-variable*
v:lua Prefix for calling lua functions from expressions.
v:lua Prefix for calling Lua functions from expressions.
See |v:lua-call| for more information.
*v:mouse_win* *mouse_win-variable*

View File

@@ -129,6 +129,7 @@ Advanced editing ~
|autocmd.txt| automatically executing commands on an event
|eval.txt| expression evaluation, conditional commands
|fold.txt| hide (fold) ranges of lines
|lua.txt| Lua API
Special issues ~
|print.txt| printing
@@ -157,7 +158,6 @@ GUI ~
Interfaces ~
|if_cscop.txt| using Cscope with Vim
|if_lua.txt| Lua interface
|if_pyth.txt| Python interface
|if_ruby.txt| Ruby interface
|sign.txt| debugging signs

View File

@@ -1,941 +1,8 @@
*if_lua.txt* Nvim
NVIM REFERENCE MANUAL
NVIM REFERENCE MANUAL
Lua engine *lua* *Lua*
Type |gO| to see the table of contents.
Moved to |lua.txt|
==============================================================================
Introduction *lua-intro*
The Lua 5.1 language is builtin and always available. Try this command to get
an idea of what lurks beneath: >
:lua print(vim.inspect(package.loaded))
Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can
be used from Lua code.
Module conflicts are resolved by "last wins". For example if both of these
are on 'runtimepath':
runtime/lua/foo.lua
~/.config/nvim/lua/foo.lua
then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and
"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim
finds and loads Lua modules. The conventions are similar to VimL plugins,
with some extra features. See |lua-require-example| for a walkthrough.
==============================================================================
Importing Lua modules *lua-require*
Nvim automatically adjusts `package.path` and `package.cpath` according to
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the
first character of `package.config`).
Similarly to `package.path`, modified directories from 'runtimepath' are also
added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing `package.cpath` are used. Example:
1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial (defined at compile-time or derived from
`$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
order: parts of the path starting from the first path component containing
question mark and preceding path separator.
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
second one contains semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
5. The cartesian product of paths from 4. and suffixes from 3. is taken,
giving four variants. In each variant `/lua` path segment is inserted
between path and suffix, leaving
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
6. New paths are prepended to the original `package.cpath`.
The result will look like this:
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
Note:
- To track 'runtimepath' updates, paths added at previous update are
remembered and removed at the next update, while all paths derived from the
new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
was reordered.
- Although adjustments happen automatically, Nvim does not track current
values of `package.path` or `package.cpath`. If you happen to delete some
paths from there you can set 'runtimepath' to trigger an update: >
let &runtimepath = &runtimepath
- Skipping paths from 'runtimepath' which contain semicolons applies both to
`package.path` and `package.cpath`. Given that there are some badly written
plugins using shell which will not work with paths containing semicolons it
is better to not have them in 'runtimepath' at all.
------------------------------------------------------------------------------
LUA PLUGIN EXAMPLE *lua-require-example*
The following example plugin adds a command `:MakeCharBlob` which transforms
current buffer into a long `unsigned char` array. Lua contains transformation
function in a module `lua/charblob.lua` which is imported in
`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
autoload/charblob.vim: >
function charblob#encode_buffer()
call setline(1, luaeval(
\ 'require("charblob").encode(unpack(_A))',
\ [getline(1, '$'), &textwidth, ' ']))
endfunction
plugin/charblob.vim: >
if exists('g:charblob_loaded')
finish
endif
let g:charblob_loaded = 1
command MakeCharBlob :call charblob#encode_buffer()
lua/charblob.lua: >
local function charblob_bytes_iter(lines)
local init_s = {
next_line_idx = 1,
next_byte_idx = 1,
lines = lines,
}
local function next(s, _)
if lines[s.next_line_idx] == nil then
return nil
end
if s.next_byte_idx > #(lines[s.next_line_idx]) then
s.next_line_idx = s.next_line_idx + 1
s.next_byte_idx = 1
return ('\n'):byte()
end
local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
if ret == ('\n'):byte() then
ret = 0 -- See :h NL-used-for-NUL.
end
s.next_byte_idx = s.next_byte_idx + 1
return ret
end
return next, init_s, nil
end
local function charblob_encode(lines, textwidth, indent)
local ret = {
'const unsigned char blob[] = {',
indent,
}
for byte in charblob_bytes_iter(lines) do
-- .- space + number (width 3) + comma
if #(ret[#ret]) + 5 > textwidth then
ret[#ret + 1] = indent
else
ret[#ret] = ret[#ret] .. ' '
end
ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
end
ret[#ret + 1] = '};'
return ret
end
return {
bytes_iter = charblob_bytes_iter,
encode = charblob_encode,
}
==============================================================================
Commands *lua-commands*
*:lua*
:[range]lua {chunk}
Execute Lua chunk {chunk}.
Examples:
>
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
<
To see the Lua version: >
:lua print(_VERSION)
To see the LuaJIT version: >
:lua print(jit.version)
<
:[range]lua << [endmarker]
{script}
{endmarker}
Execute Lua script {script}. Useful for including Lua
code in Vim scripts.
The {endmarker} must NOT be preceded by any white space.
If [endmarker] is omitted from after the "<<", a dot '.' must be used after
{script}, like for the |:append| and |:insert| commands.
Example:
>
function! CurrentLineInfo()
lua << EOF
local linenr = vim.api.nvim_win_get_cursor(0)[1]
local curline = vim.api.nvim_buf_get_lines(
0, linenr, linenr + 1, false)[1]
print(string.format("Current line [%d] has %d bytes",
linenr, #curline))
EOF
endfunction
Note that the `local` variables will disappear when block finishes. This is
not the case for globals.
*:luado*
:[range]luado {body} Execute Lua function "function (line, linenr) {body}
end" for each line in the [range], with the function
argument being set to the text of each line in turn,
without a trailing <EOL>, and the current line number.
If the value returned by the function is a string it
becomes the text of the line in the current turn. The
default for [range] is the whole file: "1,$".
Examples:
>
:luado return string.format("%s\t%d", line:reverse(), #line)
:lua require"lpeg"
:lua -- balanced parenthesis grammar:
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
:luado if bp:match(line) then return "-->\t" .. line end
<
*:luafile*
:[range]luafile {file}
Execute Lua script in {file}.
The whole argument is used as a single file name.
Examples:
>
:luafile script.lua
:luafile %
<
All these commands execute a Lua chunk from either the command line (:lua and
:luado) or a file (:luafile) with the given line [range]. Similarly to the Lua
interpreter, each chunk has its own scope and so only global variables are
shared between command calls. All Lua default libraries are available. In
addition, Lua "print" function has its output redirected to the Nvim message
area, with arguments separated by a white space instead of a tab.
Lua uses the "vim" module (see |lua-vim|) to issue commands to Nvim. However,
procedures that alter buffer content, open new buffers, and change cursor
position are restricted when the command is executed in the |sandbox|.
==============================================================================
luaeval() *lua-eval* *luaeval()*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
"luaeval". "luaeval" takes an expression string and an optional argument used
for _A inside expression and returns the result of the expression. It is
semantically equivalent in Lua to:
>
local chunkheader = "local _A = select(1, ...) return "
function luaeval (expstr, arg)
local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
return chunk(arg) -- return typval
end
Lua nils, numbers, strings, tables and booleans are converted to their
respective VimL types. An error is thrown if conversion of any other Lua types
is attempted.
The magic global "_A" contains the second argument to luaeval().
Example: >
:echo luaeval('_A[1] + _A[2]', [40, 2])
42
:echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
foo
Lua tables are used as both dictionaries and lists, so it is impossible to
determine whether empty table is meant to be empty list or empty dictionary.
Additionally Lua does not have integer numbers. To distinguish between these
cases there is the following agreement:
0. Empty table is empty list.
1. Table with N incrementally growing integral numbers, starting from 1 and
ending with N is considered to be a list.
2. Table with string keys, none of which contains NUL byte, is considered to
be a dictionary.
3. Table with string keys, at least one of which contains NUL byte, is also
considered to be a dictionary, but this time it is converted to
a |msgpack-special-map|.
*lua-special-tbl*
4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
value:
- `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
a floating-point 1.0. Note that by default integral Lua numbers are
converted to |Number|s, non-integral are converted to |Float|s. This
variant allows integral |Float|s.
- `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
converted to a dictionary `{'a': 42}`: non-string keys are ignored.
Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
are errors.
- `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
form a 1-step sequence from 1 to N are ignored, as well as all
non-integral keys.
Examples: >
:echo luaeval('math.pi')
:function Rand(x,y) " random uniform between x and y
: return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
: endfunction
:echo Rand(1,10)
Note: second argument to `luaeval` undergoes VimL to Lua conversion
("marshalled"), so changes to Lua containers do not affect values in VimL.
Return value is also always converted. When converting,
|msgpack-special-dict|s are treated specially.
==============================================================================
v:lua function calls *v:lua-call*
The special prefix `v:lua` can be used in vimL expressions to call lua
functions which are global or nested inside global tables. The expression
`v:lua.func(arg1, arg2)` is equivalent to executing the lua code
`return func(...)` where the args have been converted to lua values. In addition
`v:lua.somemod.func(args)` will work like `return somemod.func(...)` .
`v:lua` can also be used in function options like 'omnifunc'. As an
example, consider the following lua implementation of an omnifunc: >
function mymod.omnifunc(findstart, base)
if findstart == 1 then
return 0
else
return {'stuff', 'steam', 'strange things'}
end
end
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')
A limitation is that the plugin module ("mymod" in this case) must
be made available as a global.
Note: `v:lua` without a call is not allowed in a vimL expression. Funcrefs
to lua functions cannot be created. The following are errors: >
let g:Myvar = v:lua.myfunc
call SomeFunc(v:lua.mycallback)
let g:foo = v:lua
let g:foo = v:['lua']
==============================================================================
Lua standard modules *lua-stdlib*
The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
various functions and sub-modules. It is always loaded, thus require("vim")
is unnecessary.
You can peek at the module properties: >
:lua print(vim.inspect(vim))
Result is something like this: >
{
_os_proc_children = <function 1>,
_os_proc_info = <function 2>,
...
api = {
nvim__id = <function 5>,
nvim__id_array = <function 6>,
...
},
deepcopy = <function 106>,
gsplit = <function 107>,
...
}
To find documentation on e.g. the "deepcopy" function: >
:help vim.deepcopy()
Note that underscore-prefixed functions (e.g. "_os_proc_children") are
internal/private and must not be used by plugins.
------------------------------------------------------------------------------
VIM.LOOP *lua-loop* *vim.loop*
`vim.loop` exposes all features of the Nvim event-loop. This is a low-level
API that provides functionality for networking, filesystem, and process
management. Try this command to see available functions: >
:lua print(vim.inspect(vim.loop))
Reference: http://docs.libuv.org
Examples: https://github.com/luvit/luv/tree/master/examples
*E5560* *lua-loop-callbacks*
It is an error to directly invoke `vim.api` functions (except |api-fast|) in
`vim.loop` callbacks. For example, this is an error: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, function()
vim.api.nvim_command('echomsg "test"')
end)
To avoid the error use |vim.schedule_wrap()| to defer the callback: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echomsg "test"')
end))
Example: repeating timer
1. Save this code to a file.
2. Execute it with ":luafile %". >
-- Create a timer handle (implementation detail: uv_timer_t).
local timer = vim.loop.new_timer()
local i = 0
-- Waits 1000ms, then repeats every 750ms until timer:close().
timer:start(1000, 750, function()
print('timer invoked! i='..tostring(i))
if i > 4 then
timer:close() -- Always close handles to avoid leaks.
end
i = i + 1
end)
print('sleeping');
Example: File-change detection *file-change-detect*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Use ":Watch %" to watch any file.
4. Try editing the file from another text editor.
5. Observe that the file reloads in Nvim (because on_change() calls
|:checktime|). >
local w = vim.loop.new_fs_event()
local function on_change(err, fname, status)
-- Do work...
vim.api.nvim_command('checktime')
-- Debounce: stop/start.
w:stop()
watch_file(fname)
end
function watch_file(fname)
local fullpath = vim.api.nvim_call_function(
'fnamemodify', {fname, ':p'})
w:start(fullpath, {}, vim.schedule_wrap(function(...)
on_change(...) end))
end
vim.api.nvim_command(
"command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))")
Example: TCP echo-server *tcp-server*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Note the port number.
4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): >
local function create_server(host, port, on_connect)
local server = vim.loop.new_tcp()
server:bind(host, port)
server:listen(128, function(err)
assert(not err, err) -- Check for errors.
local sock = vim.loop.new_tcp()
server:accept(sock) -- Accept client connection.
on_connect(sock) -- Start reading messages.
end)
return server
end
local server = create_server('0.0.0.0', 0, function(sock)
sock:read_start(function(err, chunk)
assert(not err, err) -- Check for errors.
if chunk then
sock:write(chunk) -- Echo received messages to the channel.
else -- EOF (stream closed).
sock:close() -- Always close handles to avoid leaks.
end
end)
end)
print('TCP echo-server listening on port: '..server:getsockname().port)
------------------------------------------------------------------------------
VIM.TREESITTER *lua-treesitter*
Nvim integrates the tree-sitter library for incremental parsing of buffers.
Currently Nvim does not provide the tree-sitter parsers, instead these must
be built separately, for instance using the tree-sitter utility.
The parser is loaded into nvim using >
vim.treesitter.add_language("/path/to/c_parser.so", "c")
<Create a parser for a buffer and a given language (if another plugin uses the
same buffer/language combination, it will be safely reused). Use >
parser = vim.treesitter.get_parser(bufnr, lang)
<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this
doesn't work yet for some filetypes like "cpp") Currently, the parser will be
retained for the lifetime of a buffer but this is subject to change. A plugin
should keep a reference to the parser object as long as it wants incremental
updates.
Whenever you need to access the current syntax tree, parse the buffer: >
tstree = parser:parse()
<This will return an immutable tree that represents the current state of the
buffer. When the plugin wants to access the state after a (possible) edit
it should call `parse()` again. If the buffer wasn't edited, the same tree will
be returned again without extra work. If the buffer was parsed before,
incremental parsing will be done of the changed parts.
NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must
call `get_parser()` before you register your callback. But preferably parsing
shouldn't be done directly in the change callback anyway as they will be very
frequent. Rather a plugin that does any kind of analysis on a tree should use
a timer to throttle too frequent updates.
Tree methods *lua-treesitter-tree*
tstree:root() *tstree:root()*
Return the root node of this tree.
Node methods *lua-treesitter-node*
tsnode:parent() *tsnode:parent()*
Get the node's immediate parent.
tsnode:child_count() *tsnode:child_count()*
Get the node's number of children.
tsnode:child(N) *tsnode:child()*
Get the node's child at the given index, where zero represents the
first child.
tsnode:named_child_count() *tsnode:named_child_count()*
Get the node's number of named children.
tsnode:named_child(N) *tsnode:named_child()*
Get the node's named child at the given index, where zero represents
the first named child.
tsnode:start() *tsnode:start()*
Get the node's start position. Return three values: the row, column
and total byte count (all zero-based).
tsnode:end_() *tsnode:end_()*
Get the node's end position. Return three values: the row, column
and total byte count (all zero-based).
tsnode:range() *tsnode:range()*
Get the range of the node. Return four values: the row, column
of the start position, then the row, column of the end position.
tsnode:type() *tsnode:type()*
Get the node's type as a string.
tsnode:symbol() *tsnode:symbol()*
Get the node's type as a numerical id.
tsnode:named() *tsnode:named()*
Check if the node is named. Named nodes correspond to named rules in
the grammar, whereas anonymous nodes correspond to string literals
in the grammar.
tsnode:missing() *tsnode:missing()*
Check if the node is missing. Missing nodes are inserted by the
parser in order to recover from certain kinds of syntax errors.
tsnode:has_error() *tsnode:has_error()*
Check if the node is a syntax error or contains any syntax errors.
tsnode:sexpr() *tsnode:sexpr()*
Get an S-expression representing the node as a string.
tsnode:descendant_for_range(start_row, start_col, end_row, end_col)
*tsnode:descendant_for_range()*
Get the smallest node within this node that spans the given range of
(row, column) positions
tsnode:named_descendant_for_range(start_row, start_col, end_row, end_col)
*tsnode:named_descendant_for_range()*
Get the smallest named node within this node that spans the given
range of (row, column) positions
------------------------------------------------------------------------------
VIM *lua-builtin*
vim.api.{func}({...}) *vim.api*
Invokes Nvim |API| function {func} with arguments {...}.
Example: call the "nvim_get_current_line()" API function: >
print(tostring(vim.api.nvim_get_current_line()))
vim.call({func}, {...}) *vim.call()*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
See also |vim.fn|. Equivalent to: >
vim.fn[func]({...})
vim.in_fast_event() *vim.in_fast_event()*
Returns true if the code is executing as part of a "fast" event
handler, where most of the API is disabled. These are low-level events
(e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls
for input. When this is `false` most API functions are callable (but
may be subject to other restrictions such as |textlock|).
vim.NIL *vim.NIL*
Special value used to represent NIL in msgpack-rpc and |v:null| in
vimL interaction, and similar cases. Lua `nil` cannot be used as
part of a lua table representing a Dictionary or Array, as it
is equivalent to a missing value: `{"foo", nil}` is the same as
`{"foo"}`
vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels.
This function also works in a fast callback |lua-loop-callbacks|.
vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()*
Sends a request to {channel} to invoke {method} via
|RPC| and blocks until a response is received.
Note: NIL values as part of the return value is represented as
|vim.NIL| special value
vim.stricmp({a}, {b}) *vim.stricmp()*
Compares strings case-insensitively. Returns 0, 1 or -1 if strings
are equal, {a} is greater than {b} or {a} is lesser than {b},
respectively.
vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not
supplied, the length of the string is used. All indicies are zero-based.
Returns two values: the UTF-32 and UTF-16 indicies respectively.
Embedded NUL bytes are treated as terminating the string. Invalid
UTF-8 bytes, and embedded surrogates are counted as one code
point each. An {index} in the middle of a UTF-8 sequence is rounded
upwards to the end of that sequence.
vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()*
Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not
supplied, it defaults to false (use UTF-32). Returns the byte index.
Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index}
in the middle of a UTF-16 sequence is rounded upwards to the end of that
sequence.
vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
vim.fn.{func}({...}) *vim.fn*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
To call autoload functions, use the syntax: >
vim.fn['some#function']({...})
<
Unlike vim.api.|nvim_call_function| this converts directly between Vim
objects and Lua objects. If the Vim function returns a float, it will
be represented directly as a Lua number. Empty lists and dictionaries
both are represented by an empty table.
Note: |v:null| values as part of the return value is represented as
|vim.NIL| special value
Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only
enumerates functions that were called at least once.
vim.type_idx *vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |vim.types| allows typing the empty table (it is
unclear whether empty Lua table represents empty list or empty array)
and forcing integral numbers to be |Float|. See |lua-special-tbl| for
more details.
vim.val_idx *vim.val_idx*
Value index for tables representing |Float|s. A table representing
floating-point value 1.0 looks like this: >
{
[vim.type_idx] = vim.types.float,
[vim.val_idx] = 1.0,
}
< See also |vim.type_idx| and |lua-special-tbl|.
vim.types *vim.types*
Table with possible values for |vim.type_idx|. Contains two sets
of key-value pairs: first maps possible values for |vim.type_idx|
to human-readable strings, second maps human-readable type names to
values for |vim.type_idx|. Currently contains pairs for `float`,
`array` and `dictionary` types.
Note: one must expect that values corresponding to `vim.types.float`,
`vim.types.array` and `vim.types.dictionary` fall under only two
following assumptions:
1. Value may serve both as a key and as a value in a table. Given the
properties of Lua tables this basically means “value is not `nil`”.
2. For each value in `vim.types` table `vim.types[vim.types[value]]`
is the same as `value`.
No other restrictions are put on types, and it is not guaranteed that
values corresponding to `vim.types.float`, `vim.types.array` and
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
==============================================================================
Lua module: vim *lua-vim*
inspect({object}, {options}) *vim.inspect()*
Return a human-readable representation of the given object.
See also: ~
https://github.com/kikito/inspect.lua
https://github.com/mpeterv/vinspect
paste({lines}, {phase}) *vim.paste()*
Paste handler, invoked by |nvim_paste()| when a conforming UI
(such as the |TUI|) pastes text into the editor.
Example: To remove ANSI color codes when pasting: >
vim.paste = (function()
local overridden = vim.paste
return function(lines, phase)
for i,line in ipairs(lines) do
-- Scrub ANSI color codes from paste input.
lines[i] = line:gsub('\27%[[0-9;mK]+', '')
end
overridden(lines, phase)
end
end)()
<
Parameters: ~
{lines} |readfile()|-style list of lines to paste.
|channel-lines|
{phase} -1: "non-streaming" paste: the call contains all
lines. If paste is "streamed", `phase` indicates the stream state:
• 1: starts the paste (exactly once)
• 2: continues the paste (zero or more times)
• 3: ends the paste (exactly once)
Return: ~
false if client should cancel the paste.
See also: ~
|paste|
schedule_wrap({cb}) *vim.schedule_wrap()*
Defers callback `cb` until the Nvim API is safe to call.
See also: ~
|lua-loop-callbacks|
|vim.schedule()|
|vim.in_fast_event()|
deepcopy({orig}) *vim.deepcopy()*
Returns a deep copy of the given object. Non-table objects are
copied as in a typical Lua assignment, whereas table objects
are copied recursively.
Parameters: ~
{orig} Table to copy
Return: ~
New table of copied keys and (nested) values.
gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Splits a string at each instance of a separator.
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
Iterator over the split components
See also: ~
|vim.split()|
https://www.lua.org/pil/20.2.html
http://lua-users.org/wiki/StringLibraryTutorial
split({s}, {sep}, {plain}) *vim.split()*
Splits a string at each instance of a separator.
Examples: >
split(":aa::b:", ":") --> {'','aa','','bb',''}
split("axaby", "ab?") --> {'','x','y'}
split(x*yz*o, "*", true) --> {'x','yz','o'}
<
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
List-like table of the split components.
See also: ~
|vim.gsplit()|
tbl_contains({t}, {value}) *vim.tbl_contains()*
Checks if a list-like (vector) table contains `value` .
Parameters: ~
{t} Table to check
{value} Value to compare
Return: ~
true if `t` contains `value`
tbl_extend({behavior}, {...}) *vim.tbl_extend()*
Merges two or more map-like tables.
Parameters: ~
{behavior} Decides what to do if a key is found in more
than one map:
• "error": raise an error
• "keep": use value from the leftmost map
• "force": use value from the rightmost map
{...} Two or more map-like tables.
See also: ~
|extend()|
tbl_flatten({t}) *vim.tbl_flatten()*
Creates a copy of a list-like table such that any nested
tables are "unrolled" and appended to the result.
Parameters: ~
{t} List-like table
Return: ~
Flattened copy of the given list-like table.
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a
string.
Parameters: ~
{s} String to trim
Return: ~
String with whitespace removed from its beginning and end
See also: ~
https://www.lua.org/pil/20.2.html
pesc({s}) *vim.pesc()*
Escapes magic chars in a Lua pattern string.
Parameters: ~
{s} String to escape
Return: ~
%-escaped pattern string
See also: ~
https://github.com/rxi/lume
validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
Usage example: >
function user.new(name, age, hobbies)
vim.validate{
name={name, 'string'},
age={age, 'number'},
hobbies={hobbies, 'table'},
}
...
end
<
Examples with explicit argument values (can be run directly): >
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
=> NOP (success)
<
>
vim.validate{arg1={1, 'table'}}
=> error('arg1: expected table, got number')
<
>
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
=> error('arg1: expected even number, got 3')
<
Parameters: ~
{opt} Map of parameter names to validations. Each key is
a parameter name; each value is a tuple in one of
these forms:
1. (arg_value, type_name, optional)
• arg_value: argument value
• type_name: string type name, one of: ("table",
"t", "string", "s", "number", "n", "boolean",
"b", "function", "f", "nil", "thread",
"userdata")
• optional: (optional) boolean, if true, `nil`
is valid
2. (arg_value, fn, msg)
• arg_value: argument value
• fn: any function accepting one argument,
returns true if and only if the argument is
valid
• msg: (optional) error string if validation
fails
is_callable({f}) *vim.is_callable()*
Returns true if object `f` can be called as a function.
Parameters: ~
{f} Any object
Return: ~
true if `f` is callable, else false
vim:tw=78:ts=8:ft=help:norl:
vim:tw=78:ts=8:noet:ft=help:norl:

994
runtime/doc/lua.txt Normal file
View File

@@ -0,0 +1,994 @@
*lua.txt* Nvim
NVIM REFERENCE MANUAL
Lua engine *lua* *Lua*
Type |gO| to see the table of contents.
==============================================================================
Introduction *lua-intro*
The Lua 5.1 language is builtin and always available. Try this command to get
an idea of what lurks beneath: >
:lua print(vim.inspect(package.loaded))
Nvim includes a "standard library" |lua-stdlib| for Lua. It complements the
"editor stdlib" (|functions| and Ex commands) and the |API|, all of which can
be used from Lua code.
Module conflicts are resolved by "last wins". For example if both of these
are on 'runtimepath':
runtime/lua/foo.lua
~/.config/nvim/lua/foo.lua
then `require('foo')` loads "~/.config/nvim/lua/foo.lua", and
"runtime/lua/foo.lua" is not used. See |lua-require| to understand how Nvim
finds and loads Lua modules. The conventions are similar to VimL plugins,
with some extra features. See |lua-require-example| for a walkthrough.
==============================================================================
Importing Lua modules *lua-require*
*lua-package-path*
Nvim automatically adjusts `package.path` and `package.cpath` according to
effective 'runtimepath' value. Adjustment happens whenever 'runtimepath' is
changed. `package.path` is adjusted by simply appending `/lua/?.lua` and
`/lua/?/init.lua` to each directory from 'runtimepath' (`/` is actually the
first character of `package.config`).
Similarly to `package.path`, modified directories from 'runtimepath' are also
added to `package.cpath`. In this case, instead of appending `/lua/?.lua` and
`/lua/?/init.lua` to each runtimepath, all unique `?`-containing suffixes of
the existing `package.cpath` are used. Example:
1. Given that
- 'runtimepath' contains `/foo/bar,/xxx;yyy/baz,/abc`;
- initial (defined at compile-time or derived from
`$LUA_CPATH`/`$LUA_INIT`) `package.cpath` contains
`./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`.
2. It finds `?`-containing suffixes `/?.so`, `/a?d/j/g.elf` and `/?.so`, in
order: parts of the path starting from the first path component containing
question mark and preceding path separator.
3. The suffix of `/def/?.so`, namely `/?.so` is not unique, as its the same
as the suffix of the first path from `package.path` (i.e. `./?.so`). Which
leaves `/?.so` and `/a?d/j/g.elf`, in this order.
4. 'runtimepath' has three paths: `/foo/bar`, `/xxx;yyy/baz` and `/abc`. The
second one contains semicolon which is a paths separator so it is out,
leaving only `/foo/bar` and `/abc`, in order.
5. The cartesian product of paths from 4. and suffixes from 3. is taken,
giving four variants. In each variant `/lua` path segment is inserted
between path and suffix, leaving
- `/foo/bar/lua/?.so`
- `/foo/bar/lua/a?d/j/g.elf`
- `/abc/lua/?.so`
- `/abc/lua/a?d/j/g.elf`
6. New paths are prepended to the original `package.cpath`.
The result will look like this:
`/foo/bar,/xxx;yyy/baz,/abc` ('runtimepath')
× `./?.so;/def/ghi/a?d/j/g.elf;/def/?.so` (`package.cpath`)
= `/foo/bar/lua/?.so;/foo/bar/lua/a?d/j/g.elf;/abc/lua/?.so;/abc/lua/a?d/j/g.elf;./?.so;/def/ghi/a?d/j/g.elf;/def/?.so`
Note:
- To track 'runtimepath' updates, paths added at previous update are
remembered and removed at the next update, while all paths derived from the
new 'runtimepath' are prepended as described above. This allows removing
paths when path is removed from 'runtimepath', adding paths when they are
added and reordering `package.path`/`package.cpath` content if 'runtimepath'
was reordered.
- Although adjustments happen automatically, Nvim does not track current
values of `package.path` or `package.cpath`. If you happen to delete some
paths from there you can set 'runtimepath' to trigger an update: >
let &runtimepath = &runtimepath
- Skipping paths from 'runtimepath' which contain semicolons applies both to
`package.path` and `package.cpath`. Given that there are some badly written
plugins using shell which will not work with paths containing semicolons it
is better to not have them in 'runtimepath' at all.
------------------------------------------------------------------------------
LUA PLUGIN EXAMPLE *lua-require-example*
The following example plugin adds a command `:MakeCharBlob` which transforms
current buffer into a long `unsigned char` array. Lua contains transformation
function in a module `lua/charblob.lua` which is imported in
`autoload/charblob.vim` (`require("charblob")`). Example plugin is supposed
to be put into any directory from 'runtimepath', e.g. `~/.config/nvim` (in
this case `lua/charblob.lua` means `~/.config/nvim/lua/charblob.lua`).
autoload/charblob.vim: >
function charblob#encode_buffer()
call setline(1, luaeval(
\ 'require("charblob").encode(unpack(_A))',
\ [getline(1, '$'), &textwidth, ' ']))
endfunction
plugin/charblob.vim: >
if exists('g:charblob_loaded')
finish
endif
let g:charblob_loaded = 1
command MakeCharBlob :call charblob#encode_buffer()
lua/charblob.lua: >
local function charblob_bytes_iter(lines)
local init_s = {
next_line_idx = 1,
next_byte_idx = 1,
lines = lines,
}
local function next(s, _)
if lines[s.next_line_idx] == nil then
return nil
end
if s.next_byte_idx > #(lines[s.next_line_idx]) then
s.next_line_idx = s.next_line_idx + 1
s.next_byte_idx = 1
return ('\n'):byte()
end
local ret = lines[s.next_line_idx]:byte(s.next_byte_idx)
if ret == ('\n'):byte() then
ret = 0 -- See :h NL-used-for-NUL.
end
s.next_byte_idx = s.next_byte_idx + 1
return ret
end
return next, init_s, nil
end
local function charblob_encode(lines, textwidth, indent)
local ret = {
'const unsigned char blob[] = {',
indent,
}
for byte in charblob_bytes_iter(lines) do
-- .- space + number (width 3) + comma
if #(ret[#ret]) + 5 > textwidth then
ret[#ret + 1] = indent
else
ret[#ret] = ret[#ret] .. ' '
end
ret[#ret] = ret[#ret] .. (('%3u,'):format(byte))
end
ret[#ret + 1] = '};'
return ret
end
return {
bytes_iter = charblob_bytes_iter,
encode = charblob_encode,
}
==============================================================================
Commands *lua-commands*
These commands execute a Lua chunk from either the command line (:lua, :luado)
or a file (:luafile) on the given line [range]. As always in Lua, each chunk
has its own scope (closure), so only global variables are shared between
command calls. The |lua-stdlib| modules, user modules, and anything else on
|lua-package-path| are available.
The Lua print() function redirects its output to the Nvim message area, with
arguments separated by " " (space) instead of "\t" (tab).
*:lua*
:[range]lua {chunk}
Executes Lua chunk {chunk}.
Examples: >
:lua vim.api.nvim_command('echo "Hello, Nvim!"')
< To see the Lua version: >
:lua print(_VERSION)
< To see the LuaJIT version: >
:lua print(jit.version)
<
*:lua-heredoc*
:[range]lua << [endmarker]
{script}
{endmarker}
Executes Lua script {script} from within Vimscript.
{endmarker} must NOT be preceded by whitespace. You
can omit [endmarker] after the "<<" and use a dot "."
after {script} (similar to |:append|, |:insert|).
Example:
>
function! CurrentLineInfo()
lua << EOF
local linenr = vim.api.nvim_win_get_cursor(0)[1]
local curline = vim.api.nvim_buf_get_lines(
0, linenr, linenr + 1, false)[1]
print(string.format("Current line [%d] has %d bytes",
linenr, #curline))
EOF
endfunction
< Note that the `local` variables will disappear when
the block finishes. But not globals.
*:luado*
:[range]luado {body} Executes Lua chunk "function(line, linenr) {body} end"
for each buffer line in [range], where `line` is the
current line text (without <EOL>), and `linenr` is the
current line number. If the function returns a string
that becomes the text of the corresponding buffer
line. Default [range] is the whole file: "1,$".
Examples:
>
:luado return string.format("%s\t%d", line:reverse(), #line)
:lua require"lpeg"
:lua -- balanced parenthesis grammar:
:lua bp = lpeg.P{ "(" * ((1 - lpeg.S"()") + lpeg.V(1))^0 * ")" }
:luado if bp:match(line) then return "-->\t" .. line end
<
*:luafile*
:[range]luafile {file}
Execute Lua script in {file}.
The whole argument is used as a single file name.
Examples:
>
:luafile script.lua
:luafile %
<
==============================================================================
luaeval() *lua-eval* *luaeval()*
The (dual) equivalent of "vim.eval" for passing Lua values to Nvim is
"luaeval". "luaeval" takes an expression string and an optional argument used
for _A inside expression and returns the result of the expression. It is
semantically equivalent in Lua to:
>
local chunkheader = "local _A = select(1, ...) return "
function luaeval (expstr, arg)
local chunk = assert(loadstring(chunkheader .. expstr, "luaeval"))
return chunk(arg) -- return typval
end
Lua nils, numbers, strings, tables and booleans are converted to their
respective VimL types. An error is thrown if conversion of any other Lua types
is attempted.
The magic global "_A" contains the second argument to luaeval().
Example: >
:echo luaeval('_A[1] + _A[2]', [40, 2])
42
:echo luaeval('string.match(_A, "[a-z]+")', 'XYXfoo123')
foo
Lua tables are used as both dictionaries and lists, so it is impossible to
determine whether empty table is meant to be empty list or empty dictionary.
Additionally Lua does not have integer numbers. To distinguish between these
cases there is the following agreement:
0. Empty table is empty list.
1. Table with N incrementally growing integral numbers, starting from 1 and
ending with N is considered to be a list.
2. Table with string keys, none of which contains NUL byte, is considered to
be a dictionary.
3. Table with string keys, at least one of which contains NUL byte, is also
considered to be a dictionary, but this time it is converted to
a |msgpack-special-map|.
*lua-special-tbl*
4. Table with `vim.type_idx` key may be a dictionary, a list or floating-point
value:
- `{[vim.type_idx]=vim.types.float, [vim.val_idx]=1}` is converted to
a floating-point 1.0. Note that by default integral Lua numbers are
converted to |Number|s, non-integral are converted to |Float|s. This
variant allows integral |Float|s.
- `{[vim.type_idx]=vim.types.dictionary}` is converted to an empty
dictionary, `{[vim.type_idx]=vim.types.dictionary, [42]=1, a=2}` is
converted to a dictionary `{'a': 42}`: non-string keys are ignored.
Without `vim.type_idx` key tables with keys not fitting in 1., 2. or 3.
are errors.
- `{[vim.type_idx]=vim.types.list}` is converted to an empty list. As well
as `{[vim.type_idx]=vim.types.list, [42]=1}`: integral keys that do not
form a 1-step sequence from 1 to N are ignored, as well as all
non-integral keys.
Examples: >
:echo luaeval('math.pi')
:function Rand(x,y) " random uniform between x and y
: return luaeval('(_A.y-_A.x)*math.random()+_A.x', {'x':a:x,'y':a:y})
: endfunction
:echo Rand(1,10)
Note: second argument to `luaeval` undergoes VimL to Lua conversion
("marshalled"), so changes to Lua containers do not affect values in VimL.
Return value is also always converted. When converting,
|msgpack-special-dict|s are treated specially.
==============================================================================
Vimscript v:lua interface *v:lua-call*
From Vimscript the special `v:lua` prefix can be used to call Lua functions
which are global or accessible from global tables. The expression >
v:lua.func(arg1, arg2)
is equivalent to the Lua chunk >
return func(...)
where the args are converted to Lua values. The expression >
v:lua.somemod.func(args)
is equivalent to the Lua chunk >
return somemod.func(...)
You can use `v:lua` in "func" options like 'tagfunc', 'omnifunc', etc.
For example consider the following Lua omnifunc handler: >
function mymod.omnifunc(findstart, base)
if findstart == 1 then
return 0
else
return {'stuff', 'steam', 'strange things'}
end
end
vim.api.nvim_buf_set_option(0, 'omnifunc', 'v:lua.mymod.omnifunc')
Note: the module ("mymod" in the above example) must be a Lua global.
Note: `v:lua` without a call is not allowed in a Vimscript expression:
|Funcref|s cannot represent Lua functions. The following are errors: >
let g:Myvar = v:lua.myfunc " Error
call SomeFunc(v:lua.mycallback) " Error
let g:foo = v:lua " Error
let g:foo = v:['lua'] " Error
==============================================================================
Lua standard modules *lua-stdlib*
The Nvim Lua "standard library" (stdlib) is the `vim` module, which exposes
various functions and sub-modules. It is always loaded, thus require("vim")
is unnecessary.
You can peek at the module properties: >
:lua print(vim.inspect(vim))
Result is something like this: >
{
_os_proc_children = <function 1>,
_os_proc_info = <function 2>,
...
api = {
nvim__id = <function 5>,
nvim__id_array = <function 6>,
...
},
deepcopy = <function 106>,
gsplit = <function 107>,
...
}
To find documentation on e.g. the "deepcopy" function: >
:help vim.deepcopy()
Note that underscore-prefixed functions (e.g. "_os_proc_children") are
internal/private and must not be used by plugins.
------------------------------------------------------------------------------
VIM.LOOP *lua-loop* *vim.loop*
`vim.loop` exposes all features of the Nvim event-loop. This is a low-level
API that provides functionality for networking, filesystem, and process
management. Try this command to see available functions: >
:lua print(vim.inspect(vim.loop))
Reference: http://docs.libuv.org
Examples: https://github.com/luvit/luv/tree/master/examples
*E5560* *lua-loop-callbacks*
It is an error to directly invoke `vim.api` functions (except |api-fast|) in
`vim.loop` callbacks. For example, this is an error: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, function()
vim.api.nvim_command('echomsg "test"')
end)
To avoid the error use |vim.schedule_wrap()| to defer the callback: >
local timer = vim.loop.new_timer()
timer:start(1000, 0, vim.schedule_wrap(function()
vim.api.nvim_command('echomsg "test"')
end))
Example: repeating timer
1. Save this code to a file.
2. Execute it with ":luafile %". >
-- Create a timer handle (implementation detail: uv_timer_t).
local timer = vim.loop.new_timer()
local i = 0
-- Waits 1000ms, then repeats every 750ms until timer:close().
timer:start(1000, 750, function()
print('timer invoked! i='..tostring(i))
if i > 4 then
timer:close() -- Always close handles to avoid leaks.
end
i = i + 1
end)
print('sleeping');
Example: File-change detection *watch-file*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Use ":Watch %" to watch any file.
4. Try editing the file from another text editor.
5. Observe that the file reloads in Nvim (because on_change() calls
|:checktime|). >
local w = vim.loop.new_fs_event()
local function on_change(err, fname, status)
-- Do work...
vim.api.nvim_command('checktime')
-- Debounce: stop/start.
w:stop()
watch_file(fname)
end
function watch_file(fname)
local fullpath = vim.api.nvim_call_function(
'fnamemodify', {fname, ':p'})
w:start(fullpath, {}, vim.schedule_wrap(function(...)
on_change(...) end))
end
vim.api.nvim_command(
"command! -nargs=1 Watch call luaeval('watch_file(_A)', expand('<args>'))")
Example: TCP echo-server *tcp-server*
1. Save this code to a file.
2. Execute it with ":luafile %".
3. Note the port number.
4. Connect from any TCP client (e.g. "nc 0.0.0.0 36795"): >
local function create_server(host, port, on_connect)
local server = vim.loop.new_tcp()
server:bind(host, port)
server:listen(128, function(err)
assert(not err, err) -- Check for errors.
local sock = vim.loop.new_tcp()
server:accept(sock) -- Accept client connection.
on_connect(sock) -- Start reading messages.
end)
return server
end
local server = create_server('0.0.0.0', 0, function(sock)
sock:read_start(function(err, chunk)
assert(not err, err) -- Check for errors.
if chunk then
sock:write(chunk) -- Echo received messages to the channel.
else -- EOF (stream closed).
sock:close() -- Always close handles to avoid leaks.
end
end)
end)
print('TCP echo-server listening on port: '..server:getsockname().port)
------------------------------------------------------------------------------
VIM.TREESITTER *lua-treesitter*
Nvim integrates the tree-sitter library for incremental parsing of buffers.
Currently Nvim does not provide the tree-sitter parsers, instead these must
be built separately, for instance using the tree-sitter utility.
The parser is loaded into nvim using >
vim.treesitter.add_language("/path/to/c_parser.so", "c")
<Create a parser for a buffer and a given language (if another plugin uses the
same buffer/language combination, it will be safely reused). Use >
parser = vim.treesitter.get_parser(bufnr, lang)
<`bufnr=0` can be used for current buffer. `lang` will default to 'filetype' (this
doesn't work yet for some filetypes like "cpp") Currently, the parser will be
retained for the lifetime of a buffer but this is subject to change. A plugin
should keep a reference to the parser object as long as it wants incremental
updates.
Whenever you need to access the current syntax tree, parse the buffer: >
tstree = parser:parse()
<This will return an immutable tree that represents the current state of the
buffer. When the plugin wants to access the state after a (possible) edit
it should call `parse()` again. If the buffer wasn't edited, the same tree will
be returned again without extra work. If the buffer was parsed before,
incremental parsing will be done of the changed parts.
NB: to use the parser directly inside a |nvim_buf_attach| Lua callback, you must
call `get_parser()` before you register your callback. But preferably parsing
shouldn't be done directly in the change callback anyway as they will be very
frequent. Rather a plugin that does any kind of analysis on a tree should use
a timer to throttle too frequent updates.
Tree methods *lua-treesitter-tree*
tstree:root() *tstree:root()*
Return the root node of this tree.
Node methods *lua-treesitter-node*
tsnode:parent() *tsnode:parent()*
Get the node's immediate parent.
tsnode:child_count() *tsnode:child_count()*
Get the node's number of children.
tsnode:child(N) *tsnode:child()*
Get the node's child at the given index, where zero represents the
first child.
tsnode:named_child_count() *tsnode:named_child_count()*
Get the node's number of named children.
tsnode:named_child(N) *tsnode:named_child()*
Get the node's named child at the given index, where zero represents
the first named child.
tsnode:start() *tsnode:start()*
Get the node's start position. Return three values: the row, column
and total byte count (all zero-based).
tsnode:end_() *tsnode:end_()*
Get the node's end position. Return three values: the row, column
and total byte count (all zero-based).
tsnode:range() *tsnode:range()*
Get the range of the node. Return four values: the row, column
of the start position, then the row, column of the end position.
tsnode:type() *tsnode:type()*
Get the node's type as a string.
tsnode:symbol() *tsnode:symbol()*
Get the node's type as a numerical id.
tsnode:named() *tsnode:named()*
Check if the node is named. Named nodes correspond to named rules in
the grammar, whereas anonymous nodes correspond to string literals
in the grammar.
tsnode:missing() *tsnode:missing()*
Check if the node is missing. Missing nodes are inserted by the
parser in order to recover from certain kinds of syntax errors.
tsnode:has_error() *tsnode:has_error()*
Check if the node is a syntax error or contains any syntax errors.
tsnode:sexpr() *tsnode:sexpr()*
Get an S-expression representing the node as a string.
tsnode:descendant_for_range(start_row, start_col, end_row, end_col)
*tsnode:descendant_for_range()*
Get the smallest node within this node that spans the given range of
(row, column) positions
tsnode:named_descendant_for_range(start_row, start_col, end_row, end_col)
*tsnode:named_descendant_for_range()*
Get the smallest named node within this node that spans the given
range of (row, column) positions
------------------------------------------------------------------------------
VIM *lua-builtin*
vim.api.{func}({...}) *vim.api*
Invokes Nvim |API| function {func} with arguments {...}.
Example: call the "nvim_get_current_line()" API function: >
print(tostring(vim.api.nvim_get_current_line()))
vim.call({func}, {...}) *vim.call()*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
See also |vim.fn|. Equivalent to: >
vim.fn[func]({...})
vim.in_fast_event() *vim.in_fast_event()*
Returns true if the code is executing as part of a "fast" event
handler, where most of the API is disabled. These are low-level events
(e.g. |lua-loop-callbacks|) which can be invoked whenever Nvim polls
for input. When this is `false` most API functions are callable (but
may be subject to other restrictions such as |textlock|).
vim.NIL *vim.NIL*
Special value used to represent NIL in msgpack-rpc and |v:null| in
vimL interaction, and similar cases. Lua `nil` cannot be used as
part of a lua table representing a Dictionary or Array, as it
is equivalent to a missing value: `{"foo", nil}` is the same as
`{"foo"}`
vim.rpcnotify({channel}, {method}[, {args}...]) *vim.rpcnotify()*
Sends {event} to {channel} via |RPC| and returns immediately.
If {channel} is 0, the event is broadcast to all channels.
This function also works in a fast callback |lua-loop-callbacks|.
vim.rpcrequest({channel}, {method}[, {args}...]) *vim.rpcrequest()*
Sends a request to {channel} to invoke {method} via
|RPC| and blocks until a response is received.
Note: NIL values as part of the return value is represented as
|vim.NIL| special value
vim.stricmp({a}, {b}) *vim.stricmp()*
Compares strings case-insensitively. Returns 0, 1 or -1 if strings
are equal, {a} is greater than {b} or {a} is lesser than {b},
respectively.
vim.str_utfindex({str}[, {index}]) *vim.str_utfindex()*
Convert byte index to UTF-32 and UTF-16 indicies. If {index} is not
supplied, the length of the string is used. All indicies are zero-based.
Returns two values: the UTF-32 and UTF-16 indicies respectively.
Embedded NUL bytes are treated as terminating the string. Invalid
UTF-8 bytes, and embedded surrogates are counted as one code
point each. An {index} in the middle of a UTF-8 sequence is rounded
upwards to the end of that sequence.
vim.str_byteindex({str}, {index}[, {use_utf16}]) *vim.str_byteindex()*
Convert UTF-32 or UTF-16 {index} to byte index. If {use_utf16} is not
supplied, it defaults to false (use UTF-32). Returns the byte index.
Invalid UTF-8 and NUL is treated like by |vim.str_byteindex()|. An {index}
in the middle of a UTF-16 sequence is rounded upwards to the end of that
sequence.
vim.schedule({callback}) *vim.schedule()*
Schedules {callback} to be invoked soon by the main event-loop. Useful
to avoid |textlock| or other temporary restrictions.
vim.fn.{func}({...}) *vim.fn*
Invokes |vim-function| or |user-function| {func} with arguments {...}.
To call autoload functions, use the syntax: >
vim.fn['some#function']({...})
<
Unlike vim.api.|nvim_call_function| this converts directly between Vim
objects and Lua objects. If the Vim function returns a float, it will
be represented directly as a Lua number. Empty lists and dictionaries
both are represented by an empty table.
Note: |v:null| values as part of the return value is represented as
|vim.NIL| special value
Note: vim.fn keys are generated lazily, thus `pairs(vim.fn)` only
enumerates functions that were called at least once.
vim.type_idx *vim.type_idx*
Type index for use in |lua-special-tbl|. Specifying one of the
values from |vim.types| allows typing the empty table (it is
unclear whether empty Lua table represents empty list or empty array)
and forcing integral numbers to be |Float|. See |lua-special-tbl| for
more details.
vim.val_idx *vim.val_idx*
Value index for tables representing |Float|s. A table representing
floating-point value 1.0 looks like this: >
{
[vim.type_idx] = vim.types.float,
[vim.val_idx] = 1.0,
}
< See also |vim.type_idx| and |lua-special-tbl|.
vim.types *vim.types*
Table with possible values for |vim.type_idx|. Contains two sets
of key-value pairs: first maps possible values for |vim.type_idx|
to human-readable strings, second maps human-readable type names to
values for |vim.type_idx|. Currently contains pairs for `float`,
`array` and `dictionary` types.
Note: one must expect that values corresponding to `vim.types.float`,
`vim.types.array` and `vim.types.dictionary` fall under only two
following assumptions:
1. Value may serve both as a key and as a value in a table. Given the
properties of Lua tables this basically means “value is not `nil`”.
2. For each value in `vim.types` table `vim.types[vim.types[value]]`
is the same as `value`.
No other restrictions are put on types, and it is not guaranteed that
values corresponding to `vim.types.float`, `vim.types.array` and
`vim.types.dictionary` will not change or that `vim.types` table will
only contain values for these three types.
==============================================================================
Lua module: vim *lua-vim*
inspect({object}, {options}) *vim.inspect()*
Return a human-readable representation of the given object.
See also: ~
https://github.com/kikito/inspect.lua
https://github.com/mpeterv/vinspect
paste({lines}, {phase}) *vim.paste()*
Paste handler, invoked by |nvim_paste()| when a conforming UI
(such as the |TUI|) pastes text into the editor.
Example: To remove ANSI color codes when pasting: >
vim.paste = (function(overridden)
return function(lines, phase)
for i,line in ipairs(lines) do
-- Scrub ANSI color codes from paste input.
lines[i] = line:gsub('\27%[[0-9;mK]+', '')
end
overridden(lines, phase)
end
end)(vim.paste)
<
Parameters: ~
{lines} |readfile()|-style list of lines to paste.
|channel-lines|
{phase} -1: "non-streaming" paste: the call contains all
lines. If paste is "streamed", `phase` indicates the stream state:
• 1: starts the paste (exactly once)
• 2: continues the paste (zero or more times)
• 3: ends the paste (exactly once)
Return: ~
false if client should cancel the paste.
See also: ~
|paste|
schedule_wrap({cb}) *vim.schedule_wrap()*
Defers callback `cb` until the Nvim API is safe to call.
See also: ~
|lua-loop-callbacks|
|vim.schedule()|
|vim.in_fast_event()|
deepcopy({orig}) *vim.deepcopy()*
Returns a deep copy of the given object. Non-table objects are
copied as in a typical Lua assignment, whereas table objects
are copied recursively.
Parameters: ~
{orig} Table to copy
Return: ~
New table of copied keys and (nested) values.
gsplit({s}, {sep}, {plain}) *vim.gsplit()*
Splits a string at each instance of a separator.
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
Iterator over the split components
See also: ~
|vim.split()|
https://www.lua.org/pil/20.2.html
http://lua-users.org/wiki/StringLibraryTutorial
split({s}, {sep}, {plain}) *vim.split()*
Splits a string at each instance of a separator.
Examples: >
split(":aa::b:", ":") --> {'','aa','','bb',''}
split("axaby", "ab?") --> {'','x','y'}
split(x*yz*o, "*", true) --> {'x','yz','o'}
<
Parameters: ~
{s} String to split
{sep} Separator string or pattern
{plain} If `true` use `sep` literally (passed to
String.find)
Return: ~
List-like table of the split components.
See also: ~
|vim.gsplit()|
tbl_keys({t}) *vim.tbl_keys()*
Return a list of all keys used in a table. However, the order
of the return table of keys is not guaranteed.
Parameters: ~
{t} Table
Return: ~
list of keys
See also: ~
Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua
tbl_values({t}) *vim.tbl_values()*
Return a list of all values used in a table. However, the
order of the return table of values is not guaranteed.
Parameters: ~
{t} Table
Return: ~
list of values
tbl_contains({t}, {value}) *vim.tbl_contains()*
Checks if a list-like (vector) table contains `value` .
Parameters: ~
{t} Table to check
{value} Value to compare
Return: ~
true if `t` contains `value`
tbl_isempty({t}) *vim.tbl_isempty()*
See also: ~
Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua@paramt Table to check
tbl_extend({behavior}, {...}) *vim.tbl_extend()*
Merges two or more map-like tables.
Parameters: ~
{behavior} Decides what to do if a key is found in more
than one map:
• "error": raise an error
• "keep": use value from the leftmost map
• "force": use value from the rightmost map
{...} Two or more map-like tables.
See also: ~
|extend()|
deep_equal({a}, {b}) *vim.deep_equal()*
TODO: Documentation
tbl_add_reverse_lookup({o}) *vim.tbl_add_reverse_lookup()*
Add the reverse lookup values to an existing table. For
example: `tbl_add_reverse_lookup { A = 1 } == { [1] = 'A', A =
1 }`
Parameters: ~
{o} table The table to add the reverse to.
list_extend({dst}, {src}) *vim.list_extend()*
Extends a list-like table with the values of another list-like
table.
Parameters: ~
{dst} The list which will be modified and appended to.
{src} The list from which values will be inserted.
See also: ~
|extend()|
tbl_flatten({t}) *vim.tbl_flatten()*
Creates a copy of a list-like table such that any nested
tables are "unrolled" and appended to the result.
Parameters: ~
{t} List-like table
Return: ~
Flattened copy of the given list-like table.
See also: ~
Fromhttps://github.com/premake/premake-core/blob/master/src/base/table.lua
tbl_islist({t}) *vim.tbl_islist()*
Table
Return: ~
true: A non-empty array, false: A non-empty table, nil: An
empty table
trim({s}) *vim.trim()*
Trim whitespace (Lua pattern "%s") from both sides of a
string.
Parameters: ~
{s} String to trim
Return: ~
String with whitespace removed from its beginning and end
See also: ~
https://www.lua.org/pil/20.2.html
pesc({s}) *vim.pesc()*
Escapes magic chars in a Lua pattern string.
Parameters: ~
{s} String to escape
Return: ~
%-escaped pattern string
See also: ~
https://github.com/rxi/lume
validate({opt}) *vim.validate()*
Validates a parameter specification (types and values).
Usage example: >
function user.new(name, age, hobbies)
vim.validate{
name={name, 'string'},
age={age, 'number'},
hobbies={hobbies, 'table'},
}
...
end
<
Examples with explicit argument values (can be run directly): >
vim.validate{arg1={{'foo'}, 'table'}, arg2={'foo', 'string'}}
=> NOP (success)
<
>
vim.validate{arg1={1, 'table'}}
=> error('arg1: expected table, got number')
<
>
vim.validate{arg1={3, function(a) return (a % 2) == 0 end, 'even number'}}
=> error('arg1: expected even number, got 3')
<
Parameters: ~
{opt} Map of parameter names to validations. Each key is
a parameter name; each value is a tuple in one of
these forms:
1. (arg_value, type_name, optional)
• arg_value: argument value
• type_name: string type name, one of: ("table",
"t", "string", "s", "number", "n", "boolean",
"b", "function", "f", "nil", "thread",
"userdata")
• optional: (optional) boolean, if true, `nil`
is valid
2. (arg_value, fn, msg)
• arg_value: argument value
• fn: any function accepting one argument,
returns true if and only if the argument is
valid
• msg: (optional) error string if validation
fails
is_callable({f}) *vim.is_callable()*
Returns true if object `f` can be called as a function.
Parameters: ~
{f} Any object
Return: ~
true if `f` is callable, else false
vim:tw=78:ts=8:ft=help:norl:

View File

@@ -1,7 +1,8 @@
NVIM REFERENCE MANUAL by Thiago de Arruda
NVIM REFERENCE MANUAL
This document was merged into |api.txt| and |develop.txt|.
==============================================================================
vim:tw=78:ts=8:noet:ft=help:norl:

View File

@@ -843,6 +843,14 @@ A jump table for the options with a short description can be found at |Q_op|.
name, precede it with a backslash.
- To include a comma in a directory name precede it with a backslash.
- A directory name may end in an '/'.
- For Unix and Win32, if a directory ends in two path separators "//",
the swap file name will be built from the complete path to the file
with all path separators changed to percent '%' signs. This will
ensure file name uniqueness in the backup directory.
On Win32, it is also possible to end with "\\". However, When a
separating comma is following, you must use "//", since "\\" will
include the comma in the file name. Therefore it is recommended to
use '//', instead of '\\'.
- Environment variables are expanded |:set_env|.
- Careful with '\' characters, type one before a space, type two to
get one in the option (see |option-backslash|), for example: >
@@ -1992,12 +2000,14 @@ A jump table for the options with a short description can be found at |Q_op|.
- A directory starting with "./" (or ".\" for Windows) means to
put the swap file relative to where the edited file is. The leading
"." is replaced with the path name of the edited file.
- For Unix and Win32, if a directory ends in two path separators "//"
or "\\", the swap file name will be built from the complete path to
the file with all path separators substituted to percent '%' signs.
This will ensure file name uniqueness in the preserve directory.
On Win32, when a separating comma is following, you must use "//",
since "\\" will include the comma in the file name.
- For Unix and Win32, if a directory ends in two path separators "//",
the swap file name will be built from the complete path to the file
with all path separators substituted to percent '%' signs. This will
ensure file name uniqueness in the preserve directory.
On Win32, it is also possible to end with "\\". However, When a
separating comma is following, you must use "//", since "\\" will
include the comma in the file name. Therefore it is recommended to
use '//', instead of '\\'.
- Spaces after the comma are ignored, other spaces are considered part
of the directory name. To have a space at the start of a directory
name, precede it with a backslash.
@@ -6642,22 +6652,18 @@ A jump table for the options with a short description can be found at |Q_op|.
*'wildmenu'* *'wmnu'* *'nowildmenu'* *'nowmnu'*
'wildmenu' 'wmnu' boolean (default on)
global
When 'wildmenu' is on, command-line completion operates in an enhanced
mode. On pressing 'wildchar' (usually <Tab>) to invoke completion,
the possible matches are shown just above the command line, with the
first match highlighted (overwriting the status line, if there is
one). Keys that show the previous/next match, such as <Tab> or
CTRL-P/CTRL-N, cause the highlight to move to the appropriate match.
When 'wildmode' is used, "wildmenu" mode is used where "full" is
specified. "longest" and "list" do not start "wildmenu" mode.
You can check the current mode with |wildmenumode()|.
If there are more matches than can fit in the line, a ">" is shown on
the right and/or a "<" is shown on the left. The status line scrolls
as needed.
The "wildmenu" mode is abandoned when a key is hit that is not used
for selecting a completion.
While the "wildmenu" is active the following keys have special
meanings:
Enables "enhanced mode" of command-line completion. When user hits
<Tab> (or 'wildchar') to invoke completion, the possible matches are
shown in a menu just above the command-line (see 'wildoptions'), with
the first match highlighted (overwriting the statusline). Keys that
show the previous/next match (<Tab>/CTRL-P/CTRL-N) highlight the
match.
'wildmode' must specify "full": "longest" and "list" do not start
'wildmenu' mode. You can check the current mode with |wildmenumode()|.
The menu is canceled when a key is hit that is not used for selecting
a completion.
While the menu is active these keys have special meanings:
<Left> <Right> - select previous/next match (like CTRL-P/CTRL-N)
<Down> - in filename/menu name completion: move into a
@@ -6667,15 +6673,12 @@ A jump table for the options with a short description can be found at |Q_op|.
<Up> - in filename/menu name completion: move up into
parent directory or parent menu.
This makes the menus accessible from the console |console-menus|.
If you prefer the <Left> and <Right> keys to move the cursor instead
of selecting a different match, use this: >
If you want <Left> and <Right> to move the cursor instead of selecting
a different match, use this: >
:cnoremap <Left> <Space><BS><Left>
:cnoremap <Right> <Space><BS><Right>
<
The "WildMenu" highlighting is used for displaying the current match
|hl-WildMenu|.
|hl-WildMenu| highlights the current match.
*'wildmode'* *'wim'*
'wildmode' 'wim' string (default: "full")

View File

@@ -296,7 +296,7 @@ coerced to strings. See |id()| for more details, currently it uses
|c_CTRL-R| pasting a non-special register into |cmdline| omits the last <CR>.
Lua interface (|if_lua.txt|):
Lua interface (|lua.txt|):
- `:lua print("a\0b")` will print `a^@b`, like with `:echomsg "a\nb"` . In Vim
that prints `a` and `b` on separate lines, exactly like
@@ -307,15 +307,15 @@ Lua interface (|if_lua.txt|):
- Lua package.path and package.cpath are automatically updated according to
'runtimepath': |lua-require|.
|input()| and |inputdialog()| support for each others features (return on
cancel and completion respectively) via dictionary argument (replaces all
other arguments if used).
|input()| and |inputdialog()| support user-defined cmdline highlighting.
Commands:
|:doautocmd| does not warn about "No matching autocommands".
Functions:
|input()| and |inputdialog()| support for each others features (return on
cancel and completion respectively) via dictionary argument (replaces all
other arguments if used).
|input()| and |inputdialog()| support user-defined cmdline highlighting.
Highlight groups:
|hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other
groups
@@ -399,10 +399,10 @@ VimL (Vim script) compatibility:
Some legacy Vim features are not implemented:
- |if_py|: *python-bindeval* *python-Function* are not supported
- |if_lua|: the `vim` object is missing some legacy methods
- *if_perl*
- |if_lua|: Nvim Lua API is not compatible with Vim's "if_lua"
- *if_mzscheme*
- *if_perl*
- |if_py|: *python-bindeval* *python-Function* are not supported
- *if_tcl*
==============================================================================
@@ -524,4 +524,4 @@ TUI:
always uses 7-bit control sequences.
==============================================================================
vim:tw=78:ts=8:sw=2:noet:ft=help:norl:
vim:tw=78:ts=8:sw=2:et:ft=help:norl:

View File

@@ -20,13 +20,13 @@ setlocal wrap breakindent linebreak
setlocal nonumber norelativenumber
setlocal foldcolumn=0 colorcolumn=0 nolist nofoldenable
setlocal tagfunc=man#goto_tag
if !exists('g:no_plugin_maps') && !exists('g:no_man_maps')
nnoremap <silent> <buffer> j gj
nnoremap <silent> <buffer> k gk
nnoremap <silent> <buffer> gO :call man#show_toc()<CR>
nnoremap <silent> <buffer> <C-]> :Man<CR>
nnoremap <silent> <buffer> K :Man<CR>
nnoremap <silent> <buffer> <C-T> :call man#pop_tag()<CR>
if 1 == bufnr('%') || s:pager
nnoremap <silent> <buffer> <nowait> q :lclose<CR>:q<CR>
else

View File

@@ -24,7 +24,6 @@ local lsp = {
-- format_rpc_error = lsp_rpc.format_rpc_error;
}
-- TODO consider whether 'eol' or 'fixeol' should change the nvim_buf_get_lines that send.
-- TODO improve handling of scratch buffers with LSP attached.
local function err_message(...)
@@ -181,6 +180,14 @@ local function validate_client_config(config)
}
end
local function buf_get_full_text(bufnr)
local text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, true), '\n')
if nvim_buf_get_option(bufnr, 'eol') then
text = text .. '\n'
end
return text
end
local function text_document_did_open_handler(bufnr, client)
if not client.resolved_capabilities.text_document_open_close then
return
@@ -194,7 +201,7 @@ local function text_document_did_open_handler(bufnr, client)
uri = vim.uri_from_bufnr(bufnr);
-- TODO make sure our filetypes are compatible with languageId names.
languageId = nvim_buf_get_option(bufnr, 'filetype');
text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), '\n');
text = buf_get_full_text(bufnr);
}
}
client.notify('textDocument/didOpen', params)
@@ -557,7 +564,7 @@ do
end)
local full_changes = once(function()
return {
text = table.concat(nvim_buf_get_lines(bufnr, 0, -1, false), "\n");
text = buf_get_full_text(bufnr);
};
end)
local uri = vim.uri_from_bufnr(bufnr)

View File

@@ -85,7 +85,7 @@ CONFIG = {
'append_only': [],
},
'lua': {
'filename': 'if_lua.txt',
'filename': 'lua.txt',
'section_start_token': '*lua-vim*',
'section_order': [
'vim.lua',

View File

@@ -106,6 +106,14 @@ String buffer_get_line(Buffer buffer, Integer index, Error *err)
/// Activates buffer-update events on a channel, or as Lua callbacks.
///
/// Example (Lua): capture buffer updates in a global `events` variable
/// (use "print(vim.inspect(events))" to see its contents):
/// <pre>
/// events = {}
/// vim.api.nvim_buf_attach(0, false, {
/// on_lines=function(...) table.insert(events, {...}) end})
/// </pre>
///
/// @see |nvim_buf_detach()|
/// @see |api-buffer-updates-lua|
///
@@ -1041,18 +1049,18 @@ ArrayOf(Integer) nvim_buf_get_extmark_by_id(Buffer buffer, Integer ns_id,
/// range ends can be specified as (row, col) tuples, as well as extmark
/// ids in the same namespace. In addition, 0 and -1 works as shorthands
/// for (0,0) and (-1,-1) respectively, so that all marks in the buffer can be
/// quieried as:
/// queried as:
///
/// all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, -1)
/// all_marks = nvim_buf_get_extmarks(0, my_ns, 0, -1, {})
///
/// If end is a lower position than start, then the range will be traversed
/// backwards. This is mostly used with limited amount, to be able to get the
/// backwards. This is mostly useful with limited amount, to be able to get the
/// first marks prior to a given position.
///
/// @param buffer The buffer handle
/// @param ns_id An id returned previously from nvim_create_namespace
/// @param lower One of: extmark id, (row, col) or 0, -1 for buffer ends
/// @param upper One of: extmark id, (row, col) or 0, -1 for buffer ends
/// @param start One of: extmark id, (row, col) or 0, -1 for buffer ends
/// @param end One of: extmark id, (row, col) or 0, -1 for buffer ends
/// @param opts additional options. Supports the keys:
/// - amount: Maximum number of marks to return
/// @param[out] err Details of an error that may have occurred
@@ -1153,7 +1161,7 @@ Array nvim_buf_get_extmarks(Buffer buffer, Integer ns_id,
/// @param buffer The buffer handle
/// @param ns_id a identifier returned previously with nvim_create_namespace
/// @param id The extmark's id or 0 to create a new mark.
/// @param row The row to set the extmark to.
/// @param line The row to set the extmark to.
/// @param col The column to set the extmark to.
/// @param opts Optional parameters. Currently not used.
/// @param[out] err Details of an error that may have occurred

View File

@@ -3056,7 +3056,9 @@ static void ins_compl_clear(void)
XFREE_CLEAR(compl_orig_text);
compl_enter_selects = false;
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc());
dict_T *const d = tv_dict_alloc();
d->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_COMPLETED_ITEM, d);
}
/// Check that Insert completion is active.
@@ -4313,7 +4315,9 @@ static void ins_compl_delete(void)
// causes flicker, thus we can't do that.
changed_cline_bef_curs();
// clear v:completed_item
set_vim_var_dict(VV_COMPLETED_ITEM, tv_dict_alloc());
dict_T *const d = tv_dict_alloc();
d->dv_lock = VAR_FIXED;
set_vim_var_dict(VV_COMPLETED_ITEM, d);
}
// Insert the new text being completed.
@@ -4335,6 +4339,7 @@ static dict_T *ins_compl_dict_alloc(compl_T *match)
{
// { word, abbr, menu, kind, info }
dict_T *dict = tv_dict_alloc();
dict->dv_lock = VAR_FIXED;
tv_dict_add_str(
dict, S_LEN("word"),
(const char *)EMPTY_IF_NULL(match->cp_str));

View File

@@ -140,6 +140,31 @@ struct dbg_stuff {
except_T *current_exception;
};
typedef struct {
// parsed results
exarg_T *eap;
char_u *parsed_upto; // local we've parsed up to so far
char_u *cmd; // start of command
char_u *after_modifier;
// errors
char_u *errormsg;
// globals that need to be updated
cmdmod_T cmdmod;
int sandbox;
int msg_silent;
int emsg_silent;
bool ex_pressedreturn;
long p_verbose;
// other side-effects
bool set_eventignore;
long verbose_save;
int save_msg_silent;
int did_esilent;
bool did_sandbox;
} parse_state_T;
#ifdef INCLUDE_GENERATED_DECLARATIONS
# include "ex_docmd.c.generated.h"
@@ -1218,6 +1243,292 @@ static char_u *skip_colon_white(const char_u *p, bool skipleadingwhite)
return (char_u *)p;
}
static void parse_state_to_global(const parse_state_T *parse_state)
{
cmdmod = parse_state->cmdmod;
sandbox = parse_state->sandbox;
msg_silent = parse_state->msg_silent;
emsg_silent = parse_state->emsg_silent;
ex_pressedreturn = parse_state->ex_pressedreturn;
p_verbose = parse_state->p_verbose;
if (parse_state->set_eventignore) {
set_string_option_direct(
(char_u *)"ei", -1, (char_u *)"all", OPT_FREE, SID_NONE);
}
}
static void parse_state_from_global(parse_state_T *parse_state)
{
memset(parse_state, 0, sizeof(*parse_state));
parse_state->cmdmod = cmdmod;
parse_state->sandbox = sandbox;
parse_state->msg_silent = msg_silent;
parse_state->emsg_silent = emsg_silent;
parse_state->ex_pressedreturn = ex_pressedreturn;
parse_state->p_verbose = p_verbose;
}
//
// Parse one Ex command.
//
// This has no side-effects, except for modifying parameters
// passed in by pointer.
//
// The `out` should be zeroed, and its `ea` member initialised,
// before calling this function.
//
static bool parse_one_cmd(
char_u **cmdlinep,
parse_state_T *const out,
LineGetter fgetline,
void *fgetline_cookie)
{
exarg_T ea = {
.line1 = 1,
.line2 = 1,
};
*out->eap = ea;
// "#!anything" is handled like a comment.
if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!') {
return false;
}
/*
* Repeat until no more command modifiers are found.
*/
ea.cmd = *cmdlinep;
for (;; ) {
/*
* 1. Skip comment lines and leading white space and colons.
*/
while (*ea.cmd == ' '
|| *ea.cmd == '\t'
|| *ea.cmd == ':') {
ea.cmd++;
}
// in ex mode, an empty line works like :+
if (*ea.cmd == NUL && exmode_active
&& (getline_equal(fgetline, fgetline_cookie, getexmodeline)
|| getline_equal(fgetline, fgetline_cookie, getexline))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
ea.cmd = (char_u *)"+";
out->ex_pressedreturn = true;
}
// ignore comment and empty lines
if (*ea.cmd == '"') {
return false;
}
if (*ea.cmd == NUL) {
out->ex_pressedreturn = true;
return false;
}
/*
* 2. Handle command modifiers.
*/
char_u *p = skip_range(ea.cmd, NULL);
switch (*p) {
// When adding an entry, also modify cmd_exists().
case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3))
break;
out->cmdmod.split |= WSP_ABOVE;
continue;
case 'b': if (checkforcmd(&ea.cmd, "belowright", 3)) {
out->cmdmod.split |= WSP_BELOW;
continue;
}
if (checkforcmd(&ea.cmd, "browse", 3)) {
out->cmdmod.browse = true;
continue;
}
if (!checkforcmd(&ea.cmd, "botright", 2)) {
break;
}
out->cmdmod.split |= WSP_BOT;
continue;
case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4))
break;
out->cmdmod.confirm = true;
continue;
case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) {
out->cmdmod.keepmarks = true;
continue;
}
if (checkforcmd(&ea.cmd, "keepalt", 5)) {
out->cmdmod.keepalt = true;
continue;
}
if (checkforcmd(&ea.cmd, "keeppatterns", 5)) {
out->cmdmod.keeppatterns = true;
continue;
}
if (!checkforcmd(&ea.cmd, "keepjumps", 5)) {
break;
}
out->cmdmod.keepjumps = true;
continue;
case 'f': { // only accept ":filter {pat} cmd"
char_u *reg_pat;
if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
break;
}
if (*p == '!') {
out->cmdmod.filter_force = true;
p = skipwhite(p + 1);
if (*p == NUL || ends_excmd(*p)) {
break;
}
}
p = skip_vimgrep_pat(p, &reg_pat, NULL);
if (p == NULL || *p == NUL) {
break;
}
out->cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
if (out->cmdmod.filter_regmatch.regprog == NULL) {
break;
}
ea.cmd = p;
continue;
}
// ":hide" and ":hide | cmd" are not modifiers
case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3)
|| *p == NUL || ends_excmd(*p))
break;
ea.cmd = p;
out->cmdmod.hide = true;
continue;
case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) {
out->cmdmod.lockmarks = true;
continue;
}
if (!checkforcmd(&ea.cmd, "leftabove", 5)) {
break;
}
out->cmdmod.split |= WSP_ABOVE;
continue;
case 'n':
if (checkforcmd(&ea.cmd, "noautocmd", 3)) {
if (out->cmdmod.save_ei == NULL) {
// Set 'eventignore' to "all". Restore the
// existing option value later.
out->cmdmod.save_ei = vim_strsave(p_ei);
out->set_eventignore = true;
}
continue;
}
if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
break;
}
out->cmdmod.noswapfile = true;
continue;
case 'r': if (!checkforcmd(&ea.cmd, "rightbelow", 6))
break;
out->cmdmod.split |= WSP_BELOW;
continue;
case 's': if (checkforcmd(&ea.cmd, "sandbox", 3)) {
if (!out->did_sandbox) {
out->sandbox++;
}
out->did_sandbox = true;
continue;
}
if (!checkforcmd(&ea.cmd, "silent", 3)) {
break;
}
if (out->save_msg_silent == -1) {
out->save_msg_silent = out->msg_silent;
}
out->msg_silent++;
if (*ea.cmd == '!' && !ascii_iswhite(ea.cmd[-1])) {
// ":silent!", but not "silent !cmd"
ea.cmd = skipwhite(ea.cmd + 1);
out->emsg_silent++;
out->did_esilent++;
}
continue;
case 't': if (checkforcmd(&p, "tab", 3)) {
long tabnr = get_address(
&ea, &ea.cmd, ADDR_TABS, ea.skip, false, 1);
if (tabnr == MAXLNUM) {
out->cmdmod.tab = tabpage_index(curtab) + 1;
} else {
if (tabnr < 0 || tabnr > LAST_TAB_NR) {
out->errormsg = (char_u *)_(e_invrange);
return false;
}
out->cmdmod.tab = tabnr + 1;
}
ea.cmd = p;
continue;
}
if (!checkforcmd(&ea.cmd, "topleft", 2)) {
break;
}
out->cmdmod.split |= WSP_TOP;
continue;
case 'u': if (!checkforcmd(&ea.cmd, "unsilent", 3))
break;
if (out->save_msg_silent == -1) {
out->save_msg_silent = out->msg_silent;
}
out->msg_silent = 0;
continue;
case 'v': if (checkforcmd(&ea.cmd, "vertical", 4)) {
out->cmdmod.split |= WSP_VERT;
continue;
}
if (!checkforcmd(&p, "verbose", 4))
break;
if (out->verbose_save < 0) {
out->verbose_save = out->p_verbose;
}
if (ascii_isdigit(*ea.cmd)) {
out->p_verbose = atoi((char *)ea.cmd);
} else {
out->p_verbose = 1;
}
ea.cmd = p;
continue;
}
break;
}
out->after_modifier = ea.cmd;
// 3. Skip over the range to find the command. Let "p" point to after it.
//
// We need the command to know what kind of range it uses.
out->cmd = ea.cmd;
ea.cmd = skip_range(ea.cmd, NULL);
if (*ea.cmd == '*') {
ea.cmd = skipwhite(ea.cmd + 1);
}
out->parsed_upto = find_command(&ea, NULL);
*out->eap = ea;
return true;
}
/*
* Execute one Ex command.
*
@@ -1245,21 +1556,13 @@ static char_u * do_one_cmd(char_u **cmdlinep,
char_u *p;
linenr_T lnum;
long n;
char_u *errormsg = NULL; /* error message */
exarg_T ea; /* Ex command arguments */
long verbose_save = -1;
char_u *errormsg = NULL; // error message
exarg_T ea;
int save_msg_scroll = msg_scroll;
int save_msg_silent = -1;
int did_esilent = 0;
int did_sandbox = FALSE;
parse_state_T parsed;
cmdmod_T save_cmdmod;
const int save_reg_executing = reg_executing;
char_u *cmd;
int address_count = 1;
memset(&ea, 0, sizeof(ea));
ea.line1 = 1;
ea.line2 = 1;
ex_nesting_level++;
/* When the last file has not been edited :q has to be typed twice. */
@@ -1278,212 +1581,22 @@ static char_u * do_one_cmd(char_u **cmdlinep,
save_cmdmod = cmdmod;
memset(&cmdmod, 0, sizeof(cmdmod));
/* "#!anything" is handled like a comment. */
if ((*cmdlinep)[0] == '#' && (*cmdlinep)[1] == '!')
parse_state_from_global(&parsed);
parsed.eap = &ea;
parsed.verbose_save = -1;
parsed.save_msg_silent = -1;
parsed.did_esilent = 0;
parsed.did_sandbox = false;
bool parse_success = parse_one_cmd(cmdlinep, &parsed, fgetline, cookie);
parse_state_to_global(&parsed);
// Update locals from parse_one_cmd()
errormsg = parsed.errormsg;
p = parsed.parsed_upto;
if (!parse_success) {
goto doend;
/*
* Repeat until no more command modifiers are found.
*/
ea.cmd = *cmdlinep;
for (;; ) {
/*
* 1. Skip comment lines and leading white space and colons.
*/
while (*ea.cmd == ' ' || *ea.cmd == '\t' || *ea.cmd == ':')
++ea.cmd;
/* in ex mode, an empty line works like :+ */
if (*ea.cmd == NUL && exmode_active
&& (getline_equal(fgetline, cookie, getexmodeline)
|| getline_equal(fgetline, cookie, getexline))
&& curwin->w_cursor.lnum < curbuf->b_ml.ml_line_count) {
ea.cmd = (char_u *)"+";
ex_pressedreturn = true;
}
/* ignore comment and empty lines */
if (*ea.cmd == '"')
goto doend;
if (*ea.cmd == NUL) {
ex_pressedreturn = true;
goto doend;
}
/*
* 2. Handle command modifiers.
*/
p = skip_range(ea.cmd, NULL);
switch (*p) {
/* When adding an entry, also modify cmd_exists(). */
case 'a': if (!checkforcmd(&ea.cmd, "aboveleft", 3))
break;
cmdmod.split |= WSP_ABOVE;
continue;
case 'b': if (checkforcmd(&ea.cmd, "belowright", 3)) {
cmdmod.split |= WSP_BELOW;
continue;
}
if (checkforcmd(&ea.cmd, "browse", 3)) {
cmdmod.browse = true;
continue;
}
if (!checkforcmd(&ea.cmd, "botright", 2))
break;
cmdmod.split |= WSP_BOT;
continue;
case 'c': if (!checkforcmd(&ea.cmd, "confirm", 4))
break;
cmdmod.confirm = true;
continue;
case 'k': if (checkforcmd(&ea.cmd, "keepmarks", 3)) {
cmdmod.keepmarks = true;
continue;
}
if (checkforcmd(&ea.cmd, "keepalt", 5)) {
cmdmod.keepalt = true;
continue;
}
if (checkforcmd(&ea.cmd, "keeppatterns", 5)) {
cmdmod.keeppatterns = true;
continue;
}
if (!checkforcmd(&ea.cmd, "keepjumps", 5))
break;
cmdmod.keepjumps = true;
continue;
case 'f': { // only accept ":filter {pat} cmd"
char_u *reg_pat;
if (!checkforcmd(&p, "filter", 4) || *p == NUL || ends_excmd(*p)) {
break;
}
if (*p == '!') {
cmdmod.filter_force = true;
p = skipwhite(p + 1);
if (*p == NUL || ends_excmd(*p)) {
break;
}
}
p = skip_vimgrep_pat(p, &reg_pat, NULL);
if (p == NULL || *p == NUL) {
break;
}
cmdmod.filter_regmatch.regprog = vim_regcomp(reg_pat, RE_MAGIC);
if (cmdmod.filter_regmatch.regprog == NULL) {
break;
}
ea.cmd = p;
continue;
}
/* ":hide" and ":hide | cmd" are not modifiers */
case 'h': if (p != ea.cmd || !checkforcmd(&p, "hide", 3)
|| *p == NUL || ends_excmd(*p))
break;
ea.cmd = p;
cmdmod.hide = true;
continue;
case 'l': if (checkforcmd(&ea.cmd, "lockmarks", 3)) {
cmdmod.lockmarks = true;
continue;
}
if (!checkforcmd(&ea.cmd, "leftabove", 5))
break;
cmdmod.split |= WSP_ABOVE;
continue;
case 'n':
if (checkforcmd(&ea.cmd, "noautocmd", 3)) {
if (cmdmod.save_ei == NULL) {
/* Set 'eventignore' to "all". Restore the
* existing option value later. */
cmdmod.save_ei = vim_strsave(p_ei);
set_string_option_direct(
(char_u *)"ei", -1, (char_u *)"all", OPT_FREE, SID_NONE);
}
continue;
}
if (!checkforcmd(&ea.cmd, "noswapfile", 3)) {
break;
}
cmdmod.noswapfile = true;
continue;
case 'r': if (!checkforcmd(&ea.cmd, "rightbelow", 6))
break;
cmdmod.split |= WSP_BELOW;
continue;
case 's': if (checkforcmd(&ea.cmd, "sandbox", 3)) {
if (!did_sandbox)
++sandbox;
did_sandbox = TRUE;
continue;
}
if (!checkforcmd(&ea.cmd, "silent", 3))
break;
if (save_msg_silent == -1)
save_msg_silent = msg_silent;
++msg_silent;
if (*ea.cmd == '!' && !ascii_iswhite(ea.cmd[-1])) {
/* ":silent!", but not "silent !cmd" */
ea.cmd = skipwhite(ea.cmd + 1);
++emsg_silent;
++did_esilent;
}
continue;
case 't': if (checkforcmd(&p, "tab", 3)) {
long tabnr = get_address(&ea, &ea.cmd, ADDR_TABS, ea.skip, false, 1);
if (tabnr == MAXLNUM) {
cmdmod.tab = tabpage_index(curtab) + 1;
} else {
if (tabnr < 0 || tabnr > LAST_TAB_NR) {
errormsg = (char_u *)_(e_invrange);
goto doend;
}
cmdmod.tab = tabnr + 1;
}
ea.cmd = p;
continue;
}
if (!checkforcmd(&ea.cmd, "topleft", 2))
break;
cmdmod.split |= WSP_TOP;
continue;
case 'u': if (!checkforcmd(&ea.cmd, "unsilent", 3))
break;
if (save_msg_silent == -1)
save_msg_silent = msg_silent;
msg_silent = 0;
continue;
case 'v': if (checkforcmd(&ea.cmd, "vertical", 4)) {
cmdmod.split |= WSP_VERT;
continue;
}
if (!checkforcmd(&p, "verbose", 4))
break;
if (verbose_save < 0)
verbose_save = p_verbose;
if (ascii_isdigit(*ea.cmd))
p_verbose = atoi((char *)ea.cmd);
else
p_verbose = 1;
ea.cmd = p;
continue;
}
break;
}
char_u *after_modifier = ea.cmd;
ea.skip = (did_emsg
|| got_int
@@ -1491,17 +1604,6 @@ static char_u * do_one_cmd(char_u **cmdlinep,
|| (cstack->cs_idx >= 0
&& !(cstack->cs_flags[cstack->cs_idx] & CSF_ACTIVE)));
// 3. Skip over the range to find the command. Let "p" point to after it.
//
// We need the command to know what kind of range it uses.
cmd = ea.cmd;
ea.cmd = skip_range(ea.cmd, NULL);
if (*ea.cmd == '*') {
ea.cmd = skipwhite(ea.cmd + 1);
}
p = find_command(&ea, NULL);
// Count this line for profiling if skip is TRUE.
if (do_profiling == PROF_YES
&& (!ea.skip || cstack->cs_idx == 0
@@ -1571,148 +1673,9 @@ static char_u * do_one_cmd(char_u **cmdlinep,
}
}
/* repeat for all ',' or ';' separated addresses */
ea.cmd = cmd;
for (;; ) {
ea.line1 = ea.line2;
switch (ea.addr_type) {
case ADDR_LINES:
// default is current line number
ea.line2 = curwin->w_cursor.lnum;
break;
case ADDR_WINDOWS:
ea.line2 = CURRENT_WIN_NR;
break;
case ADDR_ARGUMENTS:
ea.line2 = curwin->w_arg_idx + 1;
if (ea.line2 > ARGCOUNT) {
ea.line2 = ARGCOUNT;
}
break;
case ADDR_LOADED_BUFFERS:
case ADDR_BUFFERS:
ea.line2 = curbuf->b_fnum;
break;
case ADDR_TABS:
ea.line2 = CURRENT_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
ea.line2 = 1;
break;
case ADDR_QUICKFIX:
ea.line2 = qf_get_cur_valid_idx(&ea);
break;
}
ea.cmd = skipwhite(ea.cmd);
lnum = get_address(&ea, &ea.cmd, ea.addr_type, ea.skip,
ea.addr_count == 0, address_count++);
if (ea.cmd == NULL) { // error detected
goto doend;
}
if (lnum == MAXLNUM) {
if (*ea.cmd == '%') { /* '%' - all lines */
++ea.cmd;
switch (ea.addr_type) {
case ADDR_LINES:
ea.line1 = 1;
ea.line2 = curbuf->b_ml.ml_line_count;
break;
case ADDR_LOADED_BUFFERS: {
buf_T *buf = firstbuf;
while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
buf = buf->b_next;
}
ea.line1 = buf->b_fnum;
buf = lastbuf;
while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
buf = buf->b_prev;
}
ea.line2 = buf->b_fnum;
break;
}
case ADDR_BUFFERS:
ea.line1 = firstbuf->b_fnum;
ea.line2 = lastbuf->b_fnum;
break;
case ADDR_WINDOWS:
case ADDR_TABS:
if (IS_USER_CMDIDX(ea.cmdidx)) {
ea.line1 = 1;
ea.line2 =
ea.addr_type == ADDR_WINDOWS ? LAST_WIN_NR : LAST_TAB_NR;
} else {
// there is no Vim command which uses '%' and
// ADDR_WINDOWS or ADDR_TABS
errormsg = (char_u *)_(e_invrange);
goto doend;
}
break;
case ADDR_TABS_RELATIVE:
errormsg = (char_u *)_(e_invrange);
goto doend;
break;
case ADDR_ARGUMENTS:
if (ARGCOUNT == 0) {
ea.line1 = ea.line2 = 0;
} else {
ea.line1 = 1;
ea.line2 = ARGCOUNT;
}
break;
case ADDR_QUICKFIX:
ea.line1 = 1;
ea.line2 = qf_get_size(&ea);
if (ea.line2 == 0) {
ea.line2 = 1;
}
break;
}
++ea.addr_count;
}
/* '*' - visual area */
else if (*ea.cmd == '*') {
pos_T *fp;
if (ea.addr_type != ADDR_LINES) {
errormsg = (char_u *)_(e_invrange);
goto doend;
}
++ea.cmd;
if (!ea.skip) {
fp = getmark('<', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
ea.line1 = fp->lnum;
fp = getmark('>', FALSE);
if (check_mark(fp) == FAIL)
goto doend;
ea.line2 = fp->lnum;
++ea.addr_count;
}
}
} else
ea.line2 = lnum;
ea.addr_count++;
if (*ea.cmd == ';') {
if (!ea.skip) {
curwin->w_cursor.lnum = ea.line2;
// don't leave the cursor on an illegal line or column
check_cursor();
}
} else if (*ea.cmd != ',') {
break;
}
ea.cmd++;
}
/* One address given: set start and end lines */
if (ea.addr_count == 1) {
ea.line1 = ea.line2;
/* ... but only implicit: really no address given */
if (lnum == MAXLNUM)
ea.addr_count = 0;
ea.cmd = parsed.cmd;
if (parse_cmd_address(&ea, &errormsg) == FAIL) {
goto doend;
}
/*
@@ -1791,8 +1754,8 @@ static char_u * do_one_cmd(char_u **cmdlinep,
if (!(flags & DOCMD_VERBOSE)) {
// If the modifier was parsed OK the error must be in the following
// command
if (after_modifier != NULL) {
append_command(after_modifier);
if (parsed.after_modifier != NULL) {
append_command(parsed.after_modifier);
} else {
append_command(*cmdlinep);
}
@@ -2260,12 +2223,12 @@ static char_u * do_one_cmd(char_u **cmdlinep,
// The :try command saves the emsg_silent flag, reset it here when
// ":silent! try" was used, it should only apply to :try itself.
if (ea.cmdidx == CMD_try && did_esilent > 0) {
emsg_silent -= did_esilent;
if (ea.cmdidx == CMD_try && parsed.did_esilent > 0) {
emsg_silent -= parsed.did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
}
did_esilent = 0;
parsed.did_esilent = 0;
}
// 7. Execute the command.
@@ -2331,8 +2294,9 @@ doend:
? cmdnames[(int)ea.cmdidx].cmd_name
: (char_u *)NULL);
if (verbose_save >= 0)
p_verbose = verbose_save;
if (parsed.verbose_save >= 0) {
p_verbose = parsed.verbose_save;
}
if (cmdmod.save_ei != NULL) {
/* Restore 'eventignore' to the value before ":noautocmd". */
set_string_option_direct((char_u *)"ei", -1, cmdmod.save_ei,
@@ -2347,16 +2311,18 @@ doend:
cmdmod = save_cmdmod;
reg_executing = save_reg_executing;
if (save_msg_silent != -1) {
/* messages could be enabled for a serious error, need to check if the
* counters don't become negative */
if (!did_emsg || msg_silent > save_msg_silent)
msg_silent = save_msg_silent;
emsg_silent -= did_esilent;
if (emsg_silent < 0)
if (parsed.save_msg_silent != -1) {
// messages could be enabled for a serious error, need to check if the
// counters don't become negative
if (!did_emsg || msg_silent > parsed.save_msg_silent) {
msg_silent = parsed.save_msg_silent;
}
emsg_silent -= parsed.did_esilent;
if (emsg_silent < 0) {
emsg_silent = 0;
/* Restore msg_scroll, it's set by file I/O commands, even when no
* message is actually displayed. */
}
// Restore msg_scroll, it's set by file I/O commands, even when no
// message is actually displayed.
msg_scroll = save_msg_scroll;
/* "silent reg" or "silent echo x" inside "redir" leaves msg_col
@@ -2365,8 +2331,9 @@ doend:
msg_col = 0;
}
if (did_sandbox)
--sandbox;
if (parsed.did_sandbox) {
sandbox--;
}
if (ea.nextcmd && *ea.nextcmd == NUL) /* not really a next command */
ea.nextcmd = NULL;
@@ -2376,6 +2343,160 @@ doend:
return ea.nextcmd;
}
// Parse the address range, if any, in "eap".
// Return FAIL and set "errormsg" or return OK.
int parse_cmd_address(exarg_T *eap, char_u **errormsg)
FUNC_ATTR_NONNULL_ALL
{
int address_count = 1;
linenr_T lnum;
// Repeat for all ',' or ';' separated addresses.
for (;;) {
eap->line1 = eap->line2;
switch (eap->addr_type) {
case ADDR_LINES:
// default is current line number
eap->line2 = curwin->w_cursor.lnum;
break;
case ADDR_WINDOWS:
eap->line2 = CURRENT_WIN_NR;
break;
case ADDR_ARGUMENTS:
eap->line2 = curwin->w_arg_idx + 1;
if (eap->line2 > ARGCOUNT) {
eap->line2 = ARGCOUNT;
}
break;
case ADDR_LOADED_BUFFERS:
case ADDR_BUFFERS:
eap->line2 = curbuf->b_fnum;
break;
case ADDR_TABS:
eap->line2 = CURRENT_TAB_NR;
break;
case ADDR_TABS_RELATIVE:
eap->line2 = 1;
break;
case ADDR_QUICKFIX:
eap->line2 = qf_get_cur_valid_idx(eap);
break;
}
eap->cmd = skipwhite(eap->cmd);
lnum = get_address(eap, &eap->cmd, eap->addr_type, eap->skip,
eap->addr_count == 0, address_count++);
if (eap->cmd == NULL) { // error detected
return FAIL;
}
if (lnum == MAXLNUM) {
if (*eap->cmd == '%') { // '%' - all lines
eap->cmd++;
switch (eap->addr_type) {
case ADDR_LINES:
eap->line1 = 1;
eap->line2 = curbuf->b_ml.ml_line_count;
break;
case ADDR_LOADED_BUFFERS: {
buf_T *buf = firstbuf;
while (buf->b_next != NULL && buf->b_ml.ml_mfp == NULL) {
buf = buf->b_next;
}
eap->line1 = buf->b_fnum;
buf = lastbuf;
while (buf->b_prev != NULL && buf->b_ml.ml_mfp == NULL) {
buf = buf->b_prev;
}
eap->line2 = buf->b_fnum;
break;
}
case ADDR_BUFFERS:
eap->line1 = firstbuf->b_fnum;
eap->line2 = lastbuf->b_fnum;
break;
case ADDR_WINDOWS:
case ADDR_TABS:
if (IS_USER_CMDIDX(eap->cmdidx)) {
eap->line1 = 1;
eap->line2 = eap->addr_type == ADDR_WINDOWS
? LAST_WIN_NR : LAST_TAB_NR;
} else {
// there is no Vim command which uses '%' and
// ADDR_WINDOWS or ADDR_TABS
*errormsg = (char_u *)_(e_invrange);
return FAIL;
}
break;
case ADDR_TABS_RELATIVE:
*errormsg = (char_u *)_(e_invrange);
return FAIL;
case ADDR_ARGUMENTS:
if (ARGCOUNT == 0) {
eap->line1 = eap->line2 = 0;
} else {
eap->line1 = 1;
eap->line2 = ARGCOUNT;
}
break;
case ADDR_QUICKFIX:
eap->line1 = 1;
eap->line2 = qf_get_size(eap);
if (eap->line2 == 0) {
eap->line2 = 1;
}
break;
}
eap->addr_count++;
} else if (*eap->cmd == '*') {
// '*' - visual area
if (eap->addr_type != ADDR_LINES) {
*errormsg = (char_u *)_(e_invrange);
return FAIL;
}
eap->cmd++;
if (!eap->skip) {
pos_T *fp = getmark('<', false);
if (check_mark(fp) == FAIL) {
return FAIL;
}
eap->line1 = fp->lnum;
fp = getmark('>', false);
if (check_mark(fp) == FAIL) {
return FAIL;
}
eap->line2 = fp->lnum;
eap->addr_count++;
}
}
} else {
eap->line2 = lnum;
}
eap->addr_count++;
if (*eap->cmd == ';') {
if (!eap->skip) {
curwin->w_cursor.lnum = eap->line2;
// don't leave the cursor on an illegal line or column
check_cursor();
}
} else if (*eap->cmd != ',') {
break;
}
eap->cmd++;
}
// One address given: set start and end lines.
if (eap->addr_count == 1) {
eap->line1 = eap->line2;
// ... but only implicit: really no address given
if (lnum == MAXLNUM) {
eap->addr_count = 0;
}
}
return OK;
}
/*
* Check for an Ex command with optional tail.
* If there is a match advance "pp" to the argument and return TRUE.
@@ -3556,15 +3677,13 @@ const char * set_one_cmd_context(
return NULL;
}
/*
* skip a range specifier of the form: addr [,addr] [;addr] ..
*
* Backslashed delimiters after / or ? will be skipped, and commands will
* not be expanded between /'s and ?'s or after "'".
*
* Also skip white space and ":" characters.
* Returns the "cmd" pointer advanced to beyond the range.
*/
// Skip a range specifier of the form: addr [,addr] [;addr] ..
//
// Backslashed delimiters after / or ? will be skipped, and commands will
// not be expanded between /'s and ?'s or after "'".
//
// Also skip white space and ":" characters.
// Returns the "cmd" pointer advanced to beyond the range.
char_u *skip_range(
const char_u *cmd,
int *ctx // pointer to xp_context or NULL

View File

@@ -2650,6 +2650,7 @@ buf_write(
*/
if (!(append && *p_pm == NUL) && !filtering && perm >= 0 && dobackup) {
FileInfo file_info;
const bool no_prepend_dot = false;
if ((bkc & BKC_YES) || append) { /* "yes" */
backup_copy = TRUE;
@@ -2737,6 +2738,7 @@ buf_write(
int some_error = false;
char_u *dirp;
char_u *rootname;
char_u *p;
/*
* Try to make the backup in each directory in the 'bdir' option.
@@ -2756,6 +2758,17 @@ buf_write(
* Isolate one directory name, using an entry in 'bdir'.
*/
(void)copy_option_part(&dirp, IObuff, IOSIZE, ",");
p = IObuff + STRLEN(IObuff);
if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) {
// Ends with '//', Use Full path
if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname))
!= NULL) {
backup = (char_u *)modname((char *)p, (char *)backup_ext,
no_prepend_dot);
xfree(p);
}
}
rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL) {
some_error = TRUE; /* out of memory */
@@ -2764,10 +2777,14 @@ buf_write(
FileInfo file_info_new;
{
/*
* Make backup file name.
*/
backup = (char_u *)modname((char *)rootname, (char *)backup_ext, FALSE);
//
// Make the backup file name.
//
if (backup == NULL) {
backup = (char_u *)modname((char *)rootname, (char *)backup_ext,
no_prepend_dot);
}
if (backup == NULL) {
xfree(rootname);
some_error = TRUE; /* out of memory */
@@ -2893,12 +2910,26 @@ nobackup:
* Isolate one directory name and make the backup file name.
*/
(void)copy_option_part(&dirp, IObuff, IOSIZE, ",");
rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL)
backup = NULL;
else {
backup = (char_u *)modname((char *)rootname, (char *)backup_ext, FALSE);
xfree(rootname);
p = IObuff + STRLEN(IObuff);
if (after_pathsep((char *)IObuff, (char *)p) && p[-1] == p[-2]) {
// path ends with '//', use full path
if ((p = (char_u *)make_percent_swname((char *)IObuff, (char *)fname))
!= NULL) {
backup = (char_u *)modname((char *)p, (char *)backup_ext,
no_prepend_dot);
xfree(p);
}
}
if (backup == NULL) {
rootname = get_file_in_dir(fname, IObuff);
if (rootname == NULL) {
backup = NULL;
} else {
backup = (char_u *)modname((char *)rootname, (char *)backup_ext,
no_prepend_dot);
xfree(rootname);
}
}
if (backup != NULL) {

View File

@@ -167,8 +167,7 @@ end
---
--- Example: To remove ANSI color codes when pasting:
--- <pre>
--- vim.paste = (function()
--- local overridden = vim.paste
--- vim.paste = (function(overridden)
--- return function(lines, phase)
--- for i,line in ipairs(lines) do
--- -- Scrub ANSI color codes from paste input.
@@ -176,7 +175,7 @@ end
--- end
--- overridden(lines, phase)
--- end
--- end)()
--- end)(vim.paste)
--- </pre>
---
--@see |paste|

View File

@@ -296,17 +296,17 @@ pos_T *movechangelist(int count)
* - NULL if there is no mark called 'c'.
* - -1 if mark is in other file and jumped there (only if changefile is TRUE)
*/
pos_T *getmark_buf(buf_T *buf, int c, int changefile)
pos_T *getmark_buf(buf_T *buf, int c, bool changefile)
{
return getmark_buf_fnum(buf, c, changefile, NULL);
}
pos_T *getmark(int c, int changefile)
pos_T *getmark(int c, bool changefile)
{
return getmark_buf_fnum(curbuf, c, changefile, NULL);
}
pos_T *getmark_buf_fnum(buf_T *buf, int c, int changefile, int *fnum)
pos_T *getmark_buf_fnum(buf_T *buf, int c, bool changefile, int *fnum)
{
pos_T *posp;
pos_T *startp, *endp;

View File

@@ -1437,7 +1437,7 @@ recover_names (
* Append the full path to name with path separators made into percent
* signs, to dir. An unnamed buffer is handled as "" (<currentdir>/"")
*/
static char *make_percent_swname(const char *dir, char *name)
char *make_percent_swname(const char *dir, char *name)
FUNC_ATTR_NONNULL_ARG(1)
{
char *d = NULL;

View File

@@ -3808,8 +3808,9 @@ current_quote(
}
vis_bef_curs = lt(VIsual, curwin->w_cursor);
vis_empty = equalpos(VIsual, curwin->w_cursor);
if (*p_sel == 'e') {
if (!vis_bef_curs) {
if (!vis_bef_curs && !vis_empty) {
// VIsual needs to be start of Visual selection.
pos_T t = curwin->w_cursor;
@@ -3819,8 +3820,8 @@ current_quote(
restore_vis_bef = true;
}
dec_cursor();
vis_empty = equalpos(VIsual, curwin->w_cursor);
}
vis_empty = equalpos(VIsual, curwin->w_cursor);
}
if (!vis_empty) {

View File

@@ -65,6 +65,7 @@ typedef struct tag_pointers {
char_u *tagkind_end; // end of tagkind
char_u *user_data; // user_data string
char_u *user_data_end; // end of user_data
linenr_T tagline; // "line:" value
} tagptrs_T;
/*
@@ -2545,6 +2546,7 @@ parse_match(
tagp->tagkind = NULL;
tagp->user_data = NULL;
tagp->tagline = 0;
tagp->command_end = NULL;
if (retval == OK) {
@@ -2564,6 +2566,8 @@ parse_match(
tagp->tagkind = p + 5;
} else if (STRNCMP(p, "user_data:", 10) == 0) {
tagp->user_data = p + 10;
} else if (STRNCMP(p, "line:", 5) == 0) {
tagp->tagline = atoi((char *)p + 5);
}
if (tagp->tagkind != NULL && tagp->user_data != NULL) {
break;
@@ -2811,7 +2815,13 @@ static int jumpto_tag(
p_ic = FALSE; /* don't ignore case now */
p_scs = FALSE;
save_lnum = curwin->w_cursor.lnum;
curwin->w_cursor.lnum = 0; /* start search before first line */
if (tagp.tagline > 0) {
// start search before line from "line:" field
curwin->w_cursor.lnum = tagp.tagline - 1;
} else {
// start search before first line
curwin->w_cursor.lnum = 0;
}
if (do_search(NULL, pbuf[0], pbuf + 1, (long)1,
search_options, NULL)) {
retval = OK;

View File

@@ -252,6 +252,8 @@ func GetVimProg()
endif
endfunc
let g:valgrind_cnt = 1
" Get the command to run Vim, with -u NONE and --headless arguments.
" If there is an argument use it instead of "NONE".
func GetVimCommand(...)
@@ -267,6 +269,13 @@ func GetVimCommand(...)
endif
let cmd .= ' --headless -i NONE'
let cmd = substitute(cmd, 'VIMRUNTIME=.*VIMRUNTIME;', '', '')
" If using valgrind, make sure every run uses a different log file.
if cmd =~ 'valgrind.*--log-file='
let cmd = substitute(cmd, '--log-file=\(^\s*\)', '--log-file=\1.' . g:valgrind_cnt, '')
let g:valgrind_cnt += 1
endif
return cmd
endfunc
@@ -290,9 +299,6 @@ endfunc
func RunVimPiped(before, after, arguments, pipecmd)
let $NVIM_LOG_FILE = exists($NVIM_LOG_FILE) ? $NVIM_LOG_FILE : 'Xnvim.log'
let cmd = GetVimCommand()
if cmd == ''
return 0
endif
let args = ''
if len(a:before) > 0
call writefile(a:before, 'Xbefore.vim')

View File

@@ -2,6 +2,7 @@
" This makes testing go faster, since Vim doesn't need to restart.
source test_assign.vim
source test_backup.vim
source test_behave.vim
source test_cd.vim
source test_changedtick.vim

View File

@@ -0,0 +1,58 @@
" Tests for the backup function
func Test_backup()
set backup backupdir=.
new
call setline(1, ['line1', 'line2'])
:f Xbackup.txt
:w! Xbackup.txt
" backup file is only created after
" writing a second time (before overwriting)
:w! Xbackup.txt
let l = readfile('Xbackup.txt~')
call assert_equal(['line1', 'line2'], l)
bw!
set backup&vim backupdir&vim
call delete('Xbackup.txt')
call delete('Xbackup.txt~')
endfunc
func Test_backup2()
set backup backupdir=.//
new
call setline(1, ['line1', 'line2', 'line3'])
:f Xbackup.txt
:w! Xbackup.txt
" backup file is only created after
" writing a second time (before overwriting)
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
let f=expand('%')
call assert_match('src%nvim%testdir%Xbackup.txt\~', f)
bw!
bw!
call delete('Xbackup.txt')
call delete(f)
set backup&vim backupdir&vim
endfunc
func Test_backup2_backupcopy()
set backup backupdir=.// backupcopy=yes
new
call setline(1, ['line1', 'line2', 'line3'])
:f Xbackup.txt
:w! Xbackup.txt
" backup file is only created after
" writing a second time (before overwriting)
:w! Xbackup.txt
sp *Xbackup.txt~
call assert_equal(['line1', 'line2', 'line3'], getline(1,'$'))
let f=expand('%')
call assert_match('src%nvim%testdir%Xbackup.txt\~', f)
bw!
bw!
call delete('Xbackup.txt')
call delete(f)
set backup&vim backupdir&vim backupcopy&vim
endfunc

View File

@@ -320,6 +320,19 @@ func Test_zz_Numbers()
\ ])
endfunc
" Affix flags
func Test_zz_affix_flags()
call LoadAffAndDic(g:test_data_aff10, g:test_data_dic10)
call RunGoodBad("drink drinkable drinkables drinktable drinkabletable",
\ "bad: drinks drinkstable drinkablestable",
\ ["drink", "drinkable", "drinkables", "table"],
\ [['bad', []],
\ ['drinks', ['drink']],
\ ['drinkstable', ['drinktable', 'drinkable', 'drink table']],
\ ['drinkablestable', ['drinkabletable', 'drinkables table', 'drinkable table']],
\ ])
endfunc
function FirstSpellWord()
call feedkeys("/^start:\n", 'tx')
normal ]smm
@@ -751,6 +764,21 @@ let g:test_data_dic9 = [
\"foo",
\"bar",
\ ]
let g:test_data_aff10 = [
\"COMPOUNDRULE se",
\"COMPOUNDPERMITFLAG p",
\"",
\"SFX A Y 1",
\"SFX A 0 able/Mp .",
\"",
\"SFX M Y 1",
\"SFX M 0 s .",
\ ]
let g:test_data_dic10 = [
\"1234",
\"drink/As",
\"table/e",
\ ]
let g:test_data_aff_sal = [
\"SET ISO8859-1",
\"TRY esianrtolcdugmphbyfvkwjkqxz-\xEB\xE9\xE8\xEA\xEF\xEE\xE4\xE0\xE2\xF6\xFC\xFB'ESIANRTOLCDUGMPHBYFVKWJKQXZ",

View File

@@ -466,4 +466,28 @@ func Test_tag_line_toolong()
let &verbose = old_vbs
endfunc
func Test_tagline()
call writefile([
\ 'provision Xtest.py /^ def provision(self, **kwargs):$/;" m line:1 language:Python class:Foo',
\ 'provision Xtest.py /^ def provision(self, **kwargs):$/;" m line:3 language:Python class:Bar',
\], 'Xtags')
call writefile([
\ ' def provision(self, **kwargs):',
\ ' pass',
\ ' def provision(self, **kwargs):',
\ ' pass',
\], 'Xtest.py')
set tags=Xtags
1tag provision
call assert_equal(line('.'), 1)
2tag provision
call assert_equal(line('.'), 3)
call delete('Xtags')
call delete('Xtest.py')
set tags&
endfunc
" vim: shiftwidth=2 sts=2 expandtab

View File

@@ -48,6 +48,9 @@ func Test_quote_selection_selection_exclusive()
set selection=exclusive
exe "norm! fdvhi'y"
call assert_equal('bcde', @")
let @"='dummy'
exe "norm! $gevi'y"
call assert_equal('bcde', @")
set selection&vim
bw!
endfunc

View File

@@ -68,9 +68,6 @@ NULL
// clang-format off
static const int included_patches[] = {
2218,
2207,
2173,
1850,
1849,
1848,
@@ -86,7 +83,7 @@ static const int included_patches[] = {
1838,
1837,
1836,
// 1835,
1835,
1834,
1833,
1832,
@@ -123,7 +120,7 @@ static const int included_patches[] = {
1801,
1800,
1799,
// 1798,
1798,
1797,
1796,
1795,
@@ -179,7 +176,7 @@ static const int included_patches[] = {
// 1745,
// 1744,
// 1743,
// 1742,
1742,
1741,
1740,
1739,
@@ -196,10 +193,10 @@ static const int included_patches[] = {
1728,
1727,
1726,
// 1725,
1725,
1724,
1723,
// 1722,
1722,
1721,
1720,
1719,
@@ -210,7 +207,7 @@ static const int included_patches[] = {
1714,
1713,
// 1712,
// 1711,
1711,
1710,
1709,
1708,
@@ -236,7 +233,7 @@ static const int included_patches[] = {
1688,
1687,
1686,
// 1685,
1685,
1684,
1683,
1682,
@@ -255,8 +252,8 @@ static const int included_patches[] = {
1669,
// 1668,
1667,
// 1666,
// 1665,
1666,
1665,
1664,
1663,
1662,
@@ -297,12 +294,12 @@ static const int included_patches[] = {
1627,
1626,
1625,
// 1624,
1624,
1623,
1622,
1621,
1620,
// 1619,
1619,
1618,
// 1617,
// 1616,
@@ -339,7 +336,7 @@ static const int included_patches[] = {
1585,
1584,
1583,
// 1582,
1582,
1581,
1580,
1579,
@@ -429,7 +426,7 @@ static const int included_patches[] = {
// 1495,
1494,
1493,
// 1492,
1492,
// 1491,
1490,
1489,
@@ -470,7 +467,7 @@ static const int included_patches[] = {
// 1454,
1453,
1452,
// 1451,
1451,
1450,
// 1449,
1448,
@@ -745,7 +742,7 @@ static const int included_patches[] = {
1179,
1178,
1177,
// 1176,
1176,
1175,
1174,
1173,
@@ -987,7 +984,7 @@ static const int included_patches[] = {
937,
936,
935,
// 934,
934,
933,
932,
931,

View File

@@ -9,9 +9,12 @@ local nvim_prog = helpers.nvim_prog
local request = helpers.request
local retry = helpers.retry
local rmdir = helpers.rmdir
local mkdir = helpers.mkdir
local sleep = helpers.sleep
local read_file = helpers.read_file
local trim = helpers.trim
local currentdir = helpers.funcs.getcwd
local iswin = helpers.iswin
describe('fileio', function()
before_each(function()
@@ -24,6 +27,7 @@ describe('fileio', function()
os.remove('Xtest_startup_file2')
os.remove('Xtest_тест.md')
rmdir('Xtest_startup_swapdir')
rmdir('Xtest_backupdir')
end)
it('fsync() codepaths #8304', function()
@@ -88,6 +92,27 @@ describe('fileio', function()
eq('foo', bar_contents);
end)
it('backup with full path #11214', function()
clear()
mkdir('Xtest_backupdir')
command('set backup')
command('set backupdir=Xtest_backupdir//')
command('write Xtest_startup_file1')
feed('ifoo<esc>')
command('write')
feed('Abar<esc>')
command('write')
-- Backup filename = fullpath, separators replaced with "%".
local backup_file_name = string.gsub(currentdir()..'/Xtest_startup_file1',
iswin() and '[:/\\]' or '/', '%%') .. '~'
local foo_contents = trim(read_file('Xtest_backupdir/'..backup_file_name))
local foobar_contents = trim(read_file('Xtest_startup_file1'))
eq('foobar', foobar_contents);
eq('foo', foo_contents);
end)
it('readfile() on multibyte filename #10586', function()
clear()
local text = {

View File

@@ -170,7 +170,7 @@ function tests.basic_check_buffer_open()
expect_notification('textDocument/didOpen', {
textDocument = {
languageId = "";
text = table.concat({"testing"; "123"}, "\n");
text = table.concat({"testing"; "123"}, "\n") .. '\n';
uri = "file://";
version = 0;
};
@@ -197,7 +197,7 @@ function tests.basic_check_buffer_open_and_change()
expect_notification('textDocument/didOpen', {
textDocument = {
languageId = "";
text = table.concat({"testing"; "123"}, "\n");
text = table.concat({"testing"; "123"}, "\n") .. '\n';
uri = "file://";
version = 0;
};
@@ -208,7 +208,7 @@ function tests.basic_check_buffer_open_and_change()
version = 3;
};
contentChanges = {
{ text = table.concat({"testing"; "boop"}, "\n"); };
{ text = table.concat({"testing"; "boop"}, "\n") .. '\n'; };
}
})
expect_notification("finish")
@@ -217,7 +217,7 @@ function tests.basic_check_buffer_open_and_change()
}
end
function tests.basic_check_buffer_open_and_change_multi()
function tests.basic_check_buffer_open_and_change_noeol()
skeleton {
on_init = function(params)
local expected_capabilities = protocol.make_client_capabilities()
@@ -244,7 +244,42 @@ function tests.basic_check_buffer_open_and_change_multi()
version = 3;
};
contentChanges = {
{ text = table.concat({"testing"; "321"}, "\n"); };
{ text = table.concat({"testing"; "boop"}, "\n"); };
}
})
expect_notification("finish")
notify('finish')
end;
}
end
function tests.basic_check_buffer_open_and_change_multi()
skeleton {
on_init = function(params)
local expected_capabilities = protocol.make_client_capabilities()
assert_eq(params.capabilities, expected_capabilities)
return {
capabilities = {
textDocumentSync = protocol.TextDocumentSyncKind.Full;
}
}
end;
body = function()
notify('start')
expect_notification('textDocument/didOpen', {
textDocument = {
languageId = "";
text = table.concat({"testing"; "123"}, "\n") .. '\n';
uri = "file://";
version = 0;
};
})
expect_notification('textDocument/didChange', {
textDocument = {
uri = "file://";
version = 3;
};
contentChanges = {
{ text = table.concat({"testing"; "321"}, "\n") .. '\n'; };
}
})
expect_notification('textDocument/didChange', {
@@ -253,7 +288,7 @@ function tests.basic_check_buffer_open_and_change_multi()
version = 4;
};
contentChanges = {
{ text = table.concat({"testing"; "boop"}, "\n"); };
{ text = table.concat({"testing"; "boop"}, "\n") .. '\n'; };
}
})
expect_notification("finish")
@@ -278,7 +313,7 @@ function tests.basic_check_buffer_open_and_change_multi_and_close()
expect_notification('textDocument/didOpen', {
textDocument = {
languageId = "";
text = table.concat({"testing"; "123"}, "\n");
text = table.concat({"testing"; "123"}, "\n") .. '\n';
uri = "file://";
version = 0;
};
@@ -289,7 +324,7 @@ function tests.basic_check_buffer_open_and_change_multi_and_close()
version = 3;
};
contentChanges = {
{ text = table.concat({"testing"; "321"}, "\n"); };
{ text = table.concat({"testing"; "321"}, "\n") .. '\n'; };
}
})
expect_notification('textDocument/didChange', {
@@ -298,7 +333,7 @@ function tests.basic_check_buffer_open_and_change_multi_and_close()
version = 4;
};
contentChanges = {
{ text = table.concat({"testing"; "boop"}, "\n"); };
{ text = table.concat({"testing"; "boop"}, "\n") .. '\n'; };
}
})
expect_notification('textDocument/didClose', {
@@ -328,7 +363,7 @@ function tests.basic_check_buffer_open_and_change_incremental()
expect_notification('textDocument/didOpen', {
textDocument = {
languageId = "";
text = table.concat({"testing"; "123"}, "\n");
text = table.concat({"testing"; "123"}, "\n") .. '\n';
uri = "file://";
version = 0;
};

View File

@@ -410,6 +410,54 @@ describe('Language Client API', function()
}
end)
it('should check the body and didChange full with noeol', function()
local expected_callbacks = {
{NIL, "shutdown", {}, 1};
{NIL, "finish", {}, 1};
{NIL, "start", {}, 1};
}
local client
test_rpc_server {
test_name = "basic_check_buffer_open_and_change_noeol";
on_setup = function()
exec_lua [[
BUFFER = vim.api.nvim_create_buf(false, true)
vim.api.nvim_buf_set_lines(BUFFER, 0, -1, false, {
"testing";
"123";
})
vim.api.nvim_buf_set_option(BUFFER, 'eol', false)
]]
end;
on_init = function(_client)
client = _client
local full_kind = exec_lua("return require'vim.lsp.protocol'.TextDocumentSyncKind.Full")
eq(full_kind, client.resolved_capabilities().text_document_did_change)
eq(true, client.resolved_capabilities().text_document_open_close)
exec_lua [[
assert(lsp.buf_attach_client(BUFFER, TEST_RPC_CLIENT_ID))
]]
end;
on_exit = function(code, signal)
eq(0, code, "exit code") eq(0, signal, "exit signal")
end;
on_callback = function(err, method, params, client_id)
if method == 'start' then
exec_lua [[
vim.api.nvim_buf_set_lines(BUFFER, 1, 2, false, {
"boop";
})
]]
client.notify('finish')
end
eq(table.remove(expected_callbacks), {err, method, params, client_id}, "expected callback")
if method == 'finish' then
client.stop()
end
end;
}
end)
-- TODO(askhan) we don't support full for now, so we can disable these tests.
pending('should check the body and didChange incremental', function()
local expected_callbacks = {

View File

@@ -153,8 +153,8 @@ set(LUAJIT_SHA256 ad5077bd861241bf5e50ae4bf543d291c5fcffab95ccc3218401131f503e45
set(LUA_URL https://www.lua.org/ftp/lua-5.1.5.tar.gz)
set(LUA_SHA256 2640fc56a795f29d28ef15e13c34a47e223960b0240e8cb0a82d9b0738695333)
set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v2.4.4.tar.gz)
set(LUAROCKS_SHA256 9eb3d0738fd02ad8bf39bcedccac4e83e9b5fff2bcca247c3584b925b2075d9c)
set(LUAROCKS_URL https://github.com/luarocks/luarocks/archive/v3.2.1.tar.gz)
set(LUAROCKS_SHA256 0cab9f79311083f33e4d8f5a76021604f1d3f7141ce9a2ef1d8b717d92058370)
set(UNIBILIUM_URL https://github.com/neovim/unibilium/archive/92d929f.tar.gz)
set(UNIBILIUM_SHA256 29815283c654277ef77a3adcc8840db79ddbb20a0f0b0c8f648bd8cd49a02e4b)

View File

@@ -52,13 +52,17 @@ if(NOT MSVC)
set(LUAROCKS_BUILDARGS CC=${HOSTDEPS_C_COMPILER} LD=${HOSTDEPS_C_COMPILER})
endif()
# Lua version, used with rocks directories.
# Defaults to 5.1 for bundled LuaJIT/Lua.
set(LUA_VERSION "5.1")
if(UNIX OR (MINGW AND CMAKE_CROSSCOMPILING))
if(USE_BUNDLED_LUAJIT)
list(APPEND LUAROCKS_OPTS
--with-lua=${HOSTDEPS_INSTALL_DIR}
--with-lua-include=${HOSTDEPS_INSTALL_DIR}/include/luajit-2.1
--lua-suffix=jit)
--with-lua-interpreter=luajit)
elseif(USE_BUNDLED_LUA)
list(APPEND LUAROCKS_OPTS
--with-lua=${HOSTDEPS_INSTALL_DIR})
@@ -66,9 +70,23 @@ if(UNIX OR (MINGW AND CMAKE_CROSSCOMPILING))
find_package(LuaJit)
if(LUAJIT_FOUND)
list(APPEND LUAROCKS_OPTS
--lua-version=5.1
--with-lua-include=${LUAJIT_INCLUDE_DIRS}
--lua-suffix=jit)
--with-lua-interpreter=luajit)
endif()
# Get LUA_VERSION used with rocks output.
if(LUAJIT_FOUND)
set(LUA_EXE "luajit")
else()
set(LUA_EXE "lua")
endif()
execute_process(
COMMAND ${LUA_EXE} -e "print(string.sub(_VERSION, 5))"
OUTPUT_VARIABLE LUA_VERSION
ERROR_VARIABLE ERR
RESULT_VARIABLE RES)
if(NOT RES EQUAL 0)
message(FATAL_ERROR "Could not get LUA_VERSION with ${LUA_EXE}: ${ERR}")
endif()
endif()
@@ -111,7 +129,7 @@ if(USE_BUNDLED_LUAJIT)
elseif(USE_BUNDLED_LUA)
add_dependencies(luarocks lua)
endif()
set(ROCKS_DIR ${HOSTDEPS_LIB_DIR}/luarocks/rocks)
set(ROCKS_DIR ${HOSTDEPS_LIB_DIR}/luarocks/rocks-${LUA_VERSION})
# mpack
add_custom_command(OUTPUT ${ROCKS_DIR}/mpack