mirror of
https://github.com/neovim/neovim.git
synced 2025-12-09 16:12:48 +00:00
Merge remote-tracking branch 'origin/master' into lsp-followup
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -10,6 +10,7 @@ compile_commands.json
|
||||
/dist/
|
||||
/.deps/
|
||||
/tmp/
|
||||
/.clangd/
|
||||
|
||||
*.mo
|
||||
.*.sw?
|
||||
|
||||
@@ -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)" }
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -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*
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 it’s 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
994
runtime/doc/lua.txt
Normal 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 it’s 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:
|
||||
@@ -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:
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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 other’s 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 other’s 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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -85,7 +85,7 @@ CONFIG = {
|
||||
'append_only': [],
|
||||
},
|
||||
'lua': {
|
||||
'filename': 'if_lua.txt',
|
||||
'filename': 'lua.txt',
|
||||
'section_start_token': '*lua-vim*',
|
||||
'section_order': [
|
||||
'vim.lua',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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, ®_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, ®_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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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|
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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
|
||||
|
||||
58
src/nvim/testdir/test_backup.vim
Normal file
58
src/nvim/testdir/test_backup.vim
Normal 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
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
4
third-party/CMakeLists.txt
vendored
4
third-party/CMakeLists.txt
vendored
@@ -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)
|
||||
|
||||
26
third-party/cmake/BuildLuarocks.cmake
vendored
26
third-party/cmake/BuildLuarocks.cmake
vendored
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user