neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

commit c05ff026bc8f4e8a1ee48b19ea384f7feb6e2d79
parent 07d0da64ed51202f0dbd92d43c89aa5f246b175e
Author: Justin M. Keyes <justinkz@gmail.com>
Date:   Thu, 23 Oct 2025 20:15:52 -0400

Merge #36028 docs


Diffstat:
Druntime/doc/debug.txt | 163-------------------------------------------------------------------------------
Mruntime/doc/dev_arch.txt | 338++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Mruntime/doc/dev_tools.txt | 300+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Mruntime/doc/develop.txt | 1+
Mruntime/doc/editing.txt | 3+++
Mruntime/doc/faq.txt | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Mruntime/doc/lsp.txt | 22+++++++++++-----------
Mruntime/doc/lua-plugin.txt | 35+++++++++++++++++++++++++++++++++++
Mruntime/doc/lua.txt | 4++--
Mruntime/doc/news.txt | 15+++++++--------
Mruntime/lua/vim/lsp.lua | 6+++---
Mruntime/lua/vim/lsp/client.lua | 6+++---
Mruntime/lua/vim/shared.lua | 4++--
Mruntime/lua/vim/treesitter/_headings.lua | 2+-
Msrc/gen/gen_help_html.lua | 1+
Msrc/nvim/README.md | 529+------------------------------------------------------------------------------
Msrc/nvim/msgpack_rpc/server.c | 2+-
Msrc/nvim/path.c | 18+++++++++---------
Mtest/functional/api/vim_spec.lua | 14+++++++-------
19 files changed, 778 insertions(+), 787 deletions(-)

diff --git a/runtime/doc/debug.txt b/runtime/doc/debug.txt @@ -1,163 +0,0 @@ -*debug.txt* Nvim - - - VIM REFERENCE MANUAL by Bram Moolenaar - - -Debugging Vim *debug-vim* - -This is for debugging Vim itself, when it doesn't work properly. -For debugging Vim scripts, functions, etc. see |debug-scripts| - - Type |gO| to see the table of contents. - -============================================================================== - -1. Location of a crash, using gcc and gdb *debug-gcc* *gdb* - -When Vim crashes in one of the test files, and you are using gcc for -compilation, here is what you can do to find out exactly where Vim crashes. -This also applies when using the MingW tools. - -1. Compile Vim with the "-g" option (there is a line in the src/Makefile for - this, which you can uncomment). Also make sure "strip" is disabled (do not - install it, or use the line "STRIP = /bin/true"). - -2. Execute these commands (replace "11" with the test that fails): > - cd testdir - gdb ../vim - run -u unix.vim -U NONE -s dotest.in test11.in - -3. Check where Vim crashes, gdb should give a message for this. - -4. Get a stack trace from gdb with this command: > - where -< You can check out different places in the stack trace with: > - frame 3 -< Replace "3" with one of the numbers in the stack trace. - -============================================================================== - -2. Locating memory leaks *debug-leaks* *valgrind* - -If you suspect Vim is leaking memory and you are using Linux, the valgrind -tool is very useful to pinpoint memory leaks. - -First of all, build Vim with EXITFREE defined. Search for this in MAKEFILE -and uncomment the line. - -Use this command to start Vim: -> - valgrind --log-file=valgrind.log --leak-check=full ./vim - -Note: Vim will run much slower. If your vimrc is big or you have several -plugins you need to be patient for startup, or run with the "-u NONE" -argument. - -There are often a few leaks from libraries, such as getpwuid() and -XtVaAppCreateShell(). Those are unavoidable. The number of bytes should be -very small a Kbyte or less. - -============================================================================== - -3. Windows Bug Reporting *debug-win32* - -If the Windows version of Vim crashes in a reproducible manner, you can take -some steps to provide a useful bug report. - - -3.1 GENERIC ~ - -You must obtain the debugger symbols (PDB) file for your executable: gvim.pdb -for gvim.exe, or vim.pdb for vim.exe. The PDB should be available from the -same place that you obtained the executable. Be sure to use the PDB that -matches the EXE (same date). - -If you built the executable yourself with the Microsoft Visual C++ compiler, -then the PDB was built with the EXE. - -If you have Visual Studio, use that instead of the VC Toolkit and WinDbg. - -For other compilers, you should always use the corresponding debugger: gdb -(see above |debug-gcc|) for the Cygwin and MinGW compilers. - - - *debug-vs2005* -3.2 Debugging Vim crashes with Visual Studio 2005/Visual C++ 2005 Express ~ - -First launch vim.exe or gvim.exe and then launch Visual Studio. (If you don't -have Visual Studio, follow the instructions at |get-ms-debuggers| to obtain a -free copy of Visual C++ 2005 Express Edition.) - -On the Tools menu, click Attach to Process. Choose the Vim process. - -In Vim, reproduce the crash. A dialog will appear in Visual Studio, telling -you about the unhandled exception in the Vim process. Click Break to break -into the process. - -Visual Studio will pop up another dialog, telling you that no symbols are -loaded and that the source code cannot be displayed. Click OK. - -Several windows will open. Right-click in the Call Stack window. Choose Load -Symbols. The Find Symbols dialog will open, looking for (g)vim.pdb. Navigate -to the directory where you have the PDB file and click Open. - -At this point, you should have a full call stack with vim function names and -line numbers. Double-click one of the lines and the Find Source dialog will -appear. Navigate to the directory where the Vim source is (if you have it.) - -If you don't know how to debug this any further, follow the instructions -at ":help bug-report". Paste the call stack into the bug report. - -If you have a non-free version of Visual Studio, you can save a minidump via -the Debug menu and send it with the bug report. A minidump is a small file -(<100KB), which contains information about the state of your process. -Visual C++ 2005 Express Edition cannot save minidumps and it cannot be -installed as a just-in-time debugger. Use WinDbg, |debug-windbg|, if you -need to save minidumps or you want a just-in-time (postmortem) debugger. - - *debug-windbg* -3.3 Debugging Vim crashes with WinDbg ~ - -See |get-ms-debuggers| to obtain a copy of WinDbg. - -As with the Visual Studio IDE, you can attach WinDbg to a running Vim process. -You can also have your system automatically invoke WinDbg as a postmortem -debugger. To set WinDbg as your postmortem debugger, run "windbg -I". - -To attach WinDbg to a running Vim process, launch WinDbg. On the File menu, -choose Attach to a Process. Select the Vim process and click OK. - -At this point, choose Symbol File Path on the File menu, and add the folder -containing your Vim PDB to the sympath. If you have Vim source available, -use Source File Path on the File menu. You can now open source files in -WinDbg and set breakpoints, if you like. Reproduce your crash. WinDbg should -open the source file at the point of the crash. Using the View menu, you can -examine the call stack, local variables, watch windows, and so on. - -If WinDbg is your postmortem debugger, you do not need to attach WinDbg to -your Vim process. Simply reproduce the crash and WinDbg will launch -automatically. As above, set the Symbol File Path and the Source File Path. - -To save a minidump, type the following at the WinDbg command line: > - .dump vim.dmp -< - *debug-minidump* -3.4 Opening a Minidump ~ - -If you have a minidump file, you can open it in Visual Studio or in WinDbg. - -In Visual Studio 2005: on the File menu, choose Open, then Project/Solution. -Navigate to the .dmp file and open it. Now press F5 to invoke the debugger. -Follow the instructions in |debug-vs2005| to set the Symbol File Path. - -In WinDbg: choose Open Crash Dump on the File menu. Follow the instructions -in |debug-windbg| to set the Symbol File Path. - - *get-ms-debuggers* -3.5 Obtaining Microsoft Debugging Tools ~ - -Visual Studio 2017 Community Edition can be downloaded for free from: - https://visualstudio.microsoft.com/downloads/ - - vim:tw=78:ts=8:noet:ft=help:norl: diff --git a/runtime/doc/dev_arch.txt b/runtime/doc/dev_arch.txt @@ -6,26 +6,79 @@ How to develop Nvim, explanation of modules and subsystems *dev-arch* -The top of each major module has (or should have) an overview in a comment at -the top of its file. The purpose of this document is to give: +Module-specific details are documented at the top of each module +(`terminal.c`, `undo.c`, …). The top of each major module has (or should have) +an overview in a comment at the top of its file. + +The purpose of this document is to give: 1. an overview of how it all fits together 2. how-to guides for common tasks such as: - - deprecating public functions - - adding a new public (API) function - - adding a new public (UI) event -3. TODO: move src/nvim/README.md into this doc. + - (TODO) deprecating public functions + - (TODO) adding a new public (API) function or (UI) event Type |gO| to see the table of contents. ============================================================================== +Filename conventions + +The source filenames use extensions to hint about their purpose. + +- `*.c`, `*.generated.c` - full C files, with all includes, etc. +- `*.c.h` - parametrized C files, contain all necessary includes, but require + defining macros before actually using. Example: `typval_encode.c.h` +- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`. +- `*.h.generated.h` - exported functions’ declarations. +- `*.c.generated.h` - static functions’ declarations. + +============================================================================== Data structures +- StringBuilder +- kvec or garray.c for dynamic lists / vectors (use StringBuilder for strings) + Use `kvec.h` for most lists. When you absolutely need a linked list, use `lib/queue_defs.h` which defines an "intrusive" linked list. +Buffer text is stored as a tree of line segments, defined in `src/nvim/memline.c`. +The central idea is found in `ml_find_line`. + +Many of the editor concepts are defined as Lua data files: + +- Events (autocmds): src/nvim/auevents.lua +- Ex (cmdline) commands: src/nvim/ex_cmds.lua +- Options: src/nvim/options.lua +- Vimscript functions: src/nvim/eval.lua +- v: variables: src/nvim/vvars.lua + +============================================================================== +Events *dev-events* + +The events historically called "autocmds", referred to here as "editor events" +or simply "events", are high-level events for use by plugins, user config, and +the Nvim editor. (There is an unrelated, low-level concept defined by the +`event/defs.h#Event` struct, which is just a bag of data passed along the +internal |event-loop|.) + +All new editor events must be implemented using `aucmd_defer()` (and where +possible, old events should be migrated to this), so that they are processed +in a predictable manner, which avoids crashes and race conditions. See +`do_markset_autocmd` for an example. + ============================================================================== -UI events +UI events *dev-ui-events* + +The long-term vision is that UI events are just another type of "editor event" +(formerly known as "autocmds"). There is no real reason that we have separate +types of user-facing or plugin-facing events. Events are events. Their +"transport" is irrelevant and any event should be possible to emit over any +transport (editor or RPC). + +Meanwhile the current situation is that UI events are a particular RPC event +packaged in a generic `redraw` notification. They also can be listened to +in-process via |vim.ui_attach()|. + +UI events are deferred to UIs, which implies a deepcopy of the UI event data. The source files most directly involved with UI events are: 1. `src/nvim/ui.*`: calls handler functions of registered UI structs (independent from msgpack-rpc) @@ -38,12 +91,8 @@ functions used by the source files above. It also generates metadata accessible as `api_info().ui_events`. See commit d3a8e9217f39c59dd7762bd22a76b8bd03ca85ff for an example of adding -a new UI event. - -UI events are deferred to UIs, which implies a deepcopy of the UI event data. - -Remember to bump NVIM_API_LEVEL if it wasn't already during this development -cycle. +a new UI event. Remember to bump NVIM_API_LEVEL if it wasn't already during +this development cycle. Other references: - |msgpack-rpc| @@ -76,5 +125,268 @@ expression evaluation. ============================================================================== +The event-loop *event-loop* + +The internal, low-level, libuv event-loop (|luv-event-loop|) is used to +schedule arbitrary work in a predictable way. One such obvious use-case for +scheduling is deferred editor-events (autocmds). Another example is +|job-control|. + +ASYNC EVENT SUPPORT + +One of the features Nvim added is the support for handling arbitrary +asynchronous events, which can include: + +- RPC requests +- job control callbacks +- timers + +Nvim implements this functionality by entering another event loop while +waiting for characters, so instead of: >py + + def state_enter(state_callback, data): + do + key = readkey() # read a key from the user + while state_callback(data, key) # invoke the callback for the current state +< + +The Nvim program loop is more like: >py + + def state_enter(state_callback, data): + do + event = read_next_event() # read an event from the operating system + while state_callback(data, event) # invoke the callback for the current state +< + +where `event` is something the operating system delivers to us, including (but +not limited to) user input. The `read_next_event()` part is internally +implemented by libuv, the platform layer used by Nvim. + +Since Nvim inherited its code from Vim, the states are not prepared to receive +"arbitrary events", so we use a special key to represent those (When a state +receives an "arbitrary event", it normally doesn't do anything other than +update the screen). + +MAIN LOOP + +The `Loop` structure (which describes `main_loop`) abstracts multiple queues +into one loop: > + + uv_loop_t uv; + MultiQueue *events; + MultiQueue *thread_events; + MultiQueue *fast_events; + +`loop_poll_events` checks `Loop.uv` and `Loop.fast_events` whenever Nvim is +idle, and also at `os_breakcheck` intervals. + +MultiQueue is cool because you can attach throw-away "child queues" trivially. +For example `do_os_system()` does this (for every spawned process!) to +automatically route events onto the `main_loop`: > + + Process *proc = &uvproc.process; + MultiQueue *events = multiqueue_new_child(main_loop.events); + proc->events = events; + + +NVIM LIFECYCLE + +How Nvim processes input. + +Consider a typical Vim-like editing session: + +01. Vim displays the welcome screen +02. User types: `:` +03. Vim enters command-line mode +04. User types: `edit README.txt<CR>` +05. Vim opens the file and returns to normal mode +06. User types: `G` +07. Vim navigates to the end of the file +09. User types: `5` +10. Vim enters count-pending mode +11. User types: `d` +12. Vim enters operator-pending mode +13. User types: `w` +14. Vim deletes 5 words +15. User types: `g` +16. Vim enters the "g command mode" +17. User types: `g` +18. Vim goes to the beginning of the file +19. User types: `i` +20. Vim enters insert mode +21. User types: `word<ESC>` +22. Vim inserts "word" at the beginning and returns to normal mode + +Note that we split user actions into sequences of inputs that change the state +of the editor. While there's no documentation about a "g command mode" (step +16), internally it is implemented similarly to "operator-pending mode". + +From this we can see that Vim has the behavior of an input-driven state machine +(more specifically, a pushdown automaton since it requires a stack for +transitioning back from states). Assuming each state has a callback responsible +for handling keys, this pseudocode represents the main program loop: >py + + def state_enter(state_callback, data): + do + key = readkey() # read a key from the user + while state_callback(data, key) # invoke the callback for the current state +< + +That is, each state is entered by calling `state_enter` and passing a +state-specific callback and data. Here is a high-level pseudocode for a program +that implements something like the workflow described above: >py + + def main() + state_enter(normal_state, {}): + + def normal_state(data, key): + if key == ':': + state_enter(command_line_state, {}) + elif key == 'i': + state_enter(insert_state, {}) + elif key == 'd': + state_enter(delete_operator_state, {}) + elif key == 'g': + state_enter(g_command_state, {}) + elif is_number(key): + state_enter(get_operator_count_state, {'count': key}) + elif key == 'G' + jump_to_eof() + return true + + def command_line_state(data, key): + if key == '<cr>': + if data['input']: + execute_ex_command(data['input']) + return false + elif key == '<esc>' + return false + + if not data['input']: + data['input'] = '' + + data['input'] += key + return true + + def delete_operator_state(data, key): + count = data['count'] or 1 + if key == 'w': + delete_word(count) + elif key == '$': + delete_to_eol(count) + return false # return to normal mode + + def g_command_state(data, key): + if key == 'g': + go_top() + elif key == 'v': + reselect() + return false # return to normal mode + + def get_operator_count_state(data, key): + if is_number(key): + data['count'] += key + return true + unshift_key(key) # return key to the input buffer + state_enter(delete_operator_state, data) + return false + + def insert_state(data, key): + if key == '<esc>': + return false # exit insert mode + self_insert(key) + return true +< + +The above gives an idea of how Nvim is organized internally. Some states like +the `g_command_state` or `get_operator_count_state` do not have a dedicated +`state_enter` callback, but are implicitly embedded into other states (this +will change later as we continue the refactoring effort). To start reading the +actual code, here's the recommended order: + +1. `state_enter()` function (state.c). This is the actual program loop, + note that a `VimState` structure is used, which contains function pointers + for the callback and state data. +2. `main()` function (main.c). After all startup, `normal_enter` is called + at the end of function to enter normal mode. +3. `normal_enter()` function (normal.c) is a small wrapper for setting + up the NormalState structure and calling `state_enter`. +4. `normal_check()` function (normal.c) is called before each iteration of + normal mode. +5. `normal_execute()` function (normal.c) is called when a key is read in normal + mode. + +The basic structure described for normal mode in 3, 4 and 5 is used for other +modes managed by the `state_enter` loop: + +- command-line mode: `command_line_{enter,check,execute}()`(`ex_getln.c`) +- insert mode: `insert_{enter,check,execute}()`(`edit.c`) +- terminal mode: `terminal_{enter,execute}()`(`terminal.c`) + +IMPORTANT VARIABLES + +The current mode is stored in `State`. The values it can have are `MODE_NORMAL`, +`MODE_INSERT`, `MODE_CMDLINE`, and a few others. + +The current window is `curwin`. The current buffer is `curbuf`. These point +to structures with the cursor position in the window, option values, the file +name, etc. + +All the global variables are declared in `globals.h`. + +THE MAIN EVENT-LOOP + +The main loop is implemented in state_enter. The basic idea is that Vim waits +for the user to type a character and processes it until another character is +needed. Thus there are several places where Vim waits for a character to be +typed. The `vgetc()` function is used for this. It also handles mapping. + +What we consider the "Nvim event loop" is actually a wrapper around `uv_run` to +handle both the `fast_events` queue and possibly (a suitable subset of) deferred +events. Therefore "raw" `vim.uv.run()` is often not enough to "yield" from Lua +plugins; instead they can call `vim.wait(0)`. + +Updating the screen is mostly postponed until a command or a sequence of +commands has finished. The work is done by `update_screen()`, which calls +`win_update()` for every window, which calls `win_line()` for every line. +See the start of [drawscreen.c](drawscreen.c) for more explanations. + +COMMAND-LINE MODE + +When typing a `:`, `normal_cmd()` will call `getcmdline()` to obtain a line with +an Ex command. `getcmdline()` calls a loop that will handle each typed +character. It returns when hitting `<CR>` or `<Esc>` or some other character that +ends the command line mode. + +EX COMMANDS + +Ex commands are handled by the function `do_cmdline()`. It does the generic +parsing of the `:` command line and calls `do_one_cmd()` for each separate +command. It also takes care of while loops. + +`do_one_cmd()` parses the range and generic arguments and puts them in the +exarg_t and passes it to the function that handles the command. + +The `:` commands are listed in [ex_cmds.lua](ex_cmds.lua). + +NORMAL MODE COMMANDS + +The Normal mode commands are handled by the `normal_cmd()` function. It also +handles the optional count and an extra character for some commands. These +are passed in a `cmdarg_T` to the function that handles the command. + +There is a table `nv_cmds` in [normal.c](normal.c) which +lists the first character of every +command. The second entry of each item is the name of the function that +handles the command. + +INSERT MODE COMMANDS + +When doing an `i` or `a` command, `normal_cmd()` will call the `edit()` function. +It contains a loop that waits for the next character and handles it. It +returns when leaving Insert mode. + + +============================================================================== vim:tw=78:ts=8:sw=4:et:ft=help:norl: diff --git a/runtime/doc/dev_tools.txt b/runtime/doc/dev_tools.txt @@ -6,15 +6,221 @@ Tools and techniques for developing Nvim *dev-tools* -The following advice is helpful when working on or debugging issues with Nvim -itself. - -TODO: merge |debug.txt| into here. +This is for developing or debugging Nvim itself. Type |gO| to see the table of contents. ============================================================================== -Backtraces *dev-tools-backtrace* +Logs *dev-tools-logs* + +Low-level log messages sink to `$NVIM_LOG_FILE`. + +UI events are logged at DEBUG level. > + + rm -rf build/ + make CMAKE_EXTRA_FLAGS="-DLOG_DEBUG" + +Use `LOG_CALLSTACK()` (Linux only) to log the current stacktrace. To log to an +alternate file (e.g. stderr) use `LOG_CALLSTACK_TO_FILE(FILE*)`. Requires +`-no-pie` ([ref](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=860394#15)): > + + rm -rf build/ + make CMAKE_EXTRA_FLAGS="-DLOG_DEBUG -DCMAKE_C_FLAGS=-no-pie" + +Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to +filter the log, e.g. at DEBUG level you might want to exclude UI messages: > + + tail -F ~/.local/state/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log + + +============================================================================== +Reproducible build + +To make a reproducible build of Nvim, set cmake variable `LUA_GEN_PRG` to +a LuaJIT binary built with `LUAJIT_SECURITY_PRN=0`. See commit +cb757f2663e6950e655c6306d713338dfa66b18d. + + +============================================================================== +Debug TUI *dev-tools-tui* + +TUI INSPECT + +Use the Ghostty https://ghostty.org/ inspector tool to observe and query the +output and events from any terminal application such as Nvim. + +TERMINFO LOGGING + +At 'verbose' level 3, Nvim logs its internal terminfo state, so you can see +exactly what terminfo values it is using on the current system. > + + nvim -V3log + +TUI DEBUGGING WITH GDB LLDB + +Launching the Nvim TUI involves two processes, one for main editor state and one +for rendering the TUI. Both of these processes use the nvim binary, so somewhat +confusingly setting a breakpoint in either will generally succeed but may not be +hit depending on which process the breakpoints were set in. + +To debug the main process, you can debug the nvim binary with the `--headless` +flag which does not launch the TUI and will allow you to set breakpoints in code +not related to TUI rendering like so: > + + lldb -- ./build/bin/nvim --headless --listen ~/.cache/nvim/debug-server.pipe + +While in lldb, enter `run`. You can then attach to the headless process in a +new terminal window to interact with the editor like so: > + + ./build/bin/nvim --remote-ui --server ~/.cache/nvim/debug-server.pipe + +Conversely for debugging TUI rendering, you can start a headless process and +debug the remote-ui process multiple times without losing editor state. + +For details on using nvim-dap and automatically debugging the child (main) +process, see [here](https://zignar.net/2023/02/17/debugging-neovim-with-neovim-and-nvim-dap/) + +TUI REDRAW + +For debugging Nvim TUI redraw behavior it is sometimes useful to slow down its +redraws. Set the 'writedelay' and 'redrawdebug' options to see where and when +the UI is painted. > + + :set writedelay=50 rdb=compositor + +Note: Nvim uses an internal screenbuffer to only send minimal updates even if a large +region is repainted internally. To also highlight excess internal redraws, use > + + :set writedelay=50 rdb=compositor,nodelta + +TUI TRACE + +In the rare case that you want to collect a trace of terminal output, the +ancient `script` command is still the "state of the art". The libvterm +`vterm-dump` utility formats the result for human-readability. + +Record a Nvim terminal session and format it with `vterm-dump`: >sh + + script foo + ./build/bin/nvim -u NONE + # Exit the script session with CTRL-d + + # Use `vterm-dump` utility to format the result. + ./.deps/usr/bin/vterm-dump foo > bar + +Then you can compare `bar` with another session, to debug TUI behavior. + +TERMINAL REFERENCE + +- `man terminfo` +- https://github.com/vim/vim/blob/0124320c97b0fbbb44613f42fc1c34fee6181fc8/src/libvterm/doc/seqs.txt +- https://invisible-island.net/xterm/ctlseqs/ctlseqs.html + +============================================================================== +Debug Performance *dev-tools-perf* + +PROFILING (EASY) + +For debugging performance bottlenecks in any code, there is a simple (and very +effective) approach: + +1. Run the slow code in a loop. +2. Break execution ~5 times and save the stacktrace. +3. The cause of the bottleneck will (almost always) appear in most of the stacktraces. + + +PROFILING (FANCY) + +For more advanced profiling, consider `perf` + `flamegraph`. + + +USDT PROFILING (POWERFUL) + +Or you can use USDT probes via `NVIM_PROBE` ([#12036](https://github.com/neovim/neovim/pull/12036)). + +> USDT is basically a way to define stable probe points in userland binaries. +> The benefit of bcc is the ability to define logic to go along with the probe +> points. + +Tools: +- bpftrace provides an awk-like language to the kernel bytecode, BPF. +- BCC provides a subset of C. Provides more complex logic than bpftrace, but takes a bit more effort. + +Example using bpftrace to track slow vim functions, and print out any files +that were opened during the trace. At the end, it prints a histogram of +function timing: > + + #!/usr/bin/env bpftrace + + BEGIN { + @depth = -1; + } + + tracepoint:sched:sched_process_fork /@pidmap[args->parent_pid]/ { + @pidmap[args->child_pid] = 1; + } + + tracepoint:sched:sched_process_exit /@pidmap[args->pid]/ { + delete(@pidmap[args->pid]); + } + + usdt:build/bin/nvim:neovim:eval__call_func__entry { + @pidmap[pid] = 1; + @depth++; + @funcentry[@depth] = nsecs; + } + + usdt:build/bin/nvim:neovim:eval__call_func__return { + $func = str(arg0); + $msecs = (nsecs - @funcentry[@depth]) / 1000000; + + @time_histo = hist($msecs); + + if ($msecs >= 1000) { + printf("%u ms for %s\n", $msecs, $func); + print(@files); + } + + clear(@files); + delete(@funcentry[@depth]); + @depth--; + } + + tracepoint:syscalls:sys_enter_open, + tracepoint:syscalls:sys_enter_openat { + if (@pidmap[pid] == 1 && @depth >= 0) { + @files[str(args->filename)] = count(); + } + } + + END { + clear(@depth); + } + + $ sudo bpftrace funcslower.bt + 1527 ms for Slower + @files[/usr/lib/libstdc++.so.6]: 2 + @files[/etc/fish/config.fish]: 2 + <snip> + + ^C + @time_histo: + [0] 71430 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| + [1] 346 | | + [2, 4) 208 | | + [4, 8) 91 | | + [8, 16) 22 | | + [16, 32) 85 | | + [32, 64) 7 | | + [64, 128) 0 | | + [128, 256) 0 | | + [256, 512) 6 | | + [512, 1K) 1 | | + [1K, 2K) 5 | | +< + +============================================================================== +Backtraces *dev-tools-backtrace* LINUX @@ -83,6 +289,53 @@ https://developer.apple.com/library/archive/technotes/tn2124/_index.html#//apple but note that some of the things on this page are out of date (such as enabling core dumps with `/etc/launchd.conf`). + +WINDOWS + +If the Windows version of Nvim crashes in a reproducible manner, you can take +some steps to provide a useful bug report. + +You must obtain the debugger symbols (PDB) file for the Nvim executable: nvim.pdb. +The PDB should be available from the same place that you obtained the +executable (TODO: not currently provided by Nvim CI releases). Be sure to use +the PDB that matches the EXE (same build). + +If you built the executable yourself with the Microsoft Visual C++ compiler, +then the PDB was built with the EXE. + +If you have Visual Studio, use that instead of the VC Toolkit and WinDbg. + +For other compilers, always use the corresponding debugger: gdb or lldb. + +Debugging Nvim crashes with Visual Studio 2005 ~ + +First launch nvim.exe and then launch Visual Studio. (If you don't have +Visual Studio, get it from https://visualstudio.microsoft.com/downloads/). + +On the Tools menu, click Attach to Process. Choose the Nvim process. + +In Nvim, reproduce the crash. A dialog will appear in Visual Studio, telling +you about the unhandled exception in the Nvim process. Click Break to break +into the process. + +Visual Studio will pop up another dialog, telling you that no symbols are +loaded and that the source code cannot be displayed. Click OK. + +Several windows will open. Right-click in the Call Stack window. Choose Load +Symbols. The Find Symbols dialog will open, looking for (g)vim.pdb. Navigate +to the directory where you have the PDB file and click Open. + +At this point, you should have a full call stack with vim function names and +line numbers. Double-click one of the lines and the Find Source dialog will +navigate to the directory where the Nvim source is (if you have it.) + +If you don't know how to debug this any further, follow the instructions +at ":help bug-report". Paste the call stack into the bug report. + +From Visual Studio you can also try saving a minidump via the Debug menu and +send it with the bug report. A minidump is a small file (<100KB), which +contains information about the state of your process. + ============================================================================== Gdb *dev-tools-gdb* @@ -194,5 +447,42 @@ example: (gdb) target remote localhost:6666 (gdb) br main < +============================================================================== +Debugging crashes or memory leaks *dev-tools-asan* + +BUILD WITH ASAN + +Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined +Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is +a good way to catch undefined behavior, leaks and other errors as soon as they +happen. It's significantly faster than Valgrind. + +Requires clang 3.4 or later, and `llvm-symbolizer` must be in `$PATH`: > + + clang --version + +Build Nvim with sanitizer instrumentation (choose one): > + + CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_ASAN_UBSAN=ON" + CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_MSAN=ON" + CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_TSAN=ON" + +Create a directory to store logs: > + + mkdir -p "$HOME/logs" + +Configure the sanitizer(s) via these environment variables: > + + # Change to detect_leaks=1 to detect memory leaks (slower, noisier). + export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" + # Show backtraces in the logs. + export MSAN_OPTIONS="log_path=${HOME}/logs/msan" + export TSAN_OPTIONS="log_path=${HOME}/logs/tsan" + +Logs will be written to `${HOME}/logs/*san.PID` then. + +For more information: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags + + vim:tw=78:ts=8:et:ft=help:norl: diff --git a/runtime/doc/develop.txt b/runtime/doc/develop.txt @@ -475,6 +475,7 @@ everywhere, not "buffer" in some places and "buf" in others. - chan: |channel| - cmd: Command - cmdline: Command-line UI or input + - dir: Directory - fn: Function - hl: Highlight - pos: Position diff --git a/runtime/doc/editing.txt b/runtime/doc/editing.txt @@ -87,6 +87,9 @@ g CTRL-G Prints the current position of the cursor in five If the buffer did have a name, that name becomes the |alternate-file| name. An unlisted buffer is created to hold the old name. + + See |nvim_buf_set_name()| to avoid filename escaping. + *:0file* :0f[ile][!] Remove the name of the current buffer. The optional ! avoids truncating the message, as with |:file|. diff --git a/runtime/doc/faq.txt b/runtime/doc/faq.txt @@ -11,12 +11,14 @@ Frequently asked Questions *faq* General Questions *faq-general* -WHERE SHOULD I PUT MY CONFIG (VIMRC)? ~ +------------------------------------------------------------------------------ +WHERE SHOULD I PUT MY CONFIG (VIMRC)? See |config|; you can copy (or symlink) your existing vimrc. |nvim-from-vim| -HOW STABLE IS THE DEVELOPMENT (PRE-RELEASE) VERSION? ~ +------------------------------------------------------------------------------ +HOW STABLE IS THE DEVELOPMENT (PRE-RELEASE) VERSION? The unstable (pre-release) https://github.com/neovim/neovim/releases/tag/nightly version of Nvim @@ -29,14 +31,16 @@ Use the stable (release) https://github.com/neovim/neovim/releases/latest version for a more predictable experience. -CAN I USE LUA-BASED VIM PLUGINS (E.G. NEOCOMPLETE)? ~ +------------------------------------------------------------------------------ +CAN I USE LUA-BASED VIM PLUGINS (E.G. NEOCOMPLETE)? No. Starting with Nvim 0.2 PR #4411 https://github.com/neovim/neovim/pull/4411 Lua is built-in, but the legacy Vim `if_lua` interface is not supported. -HOW CAN I USE "TRUE COLOR" IN THE TERMINAL? ~ +------------------------------------------------------------------------------ +HOW CAN I USE "TRUE COLOR" IN THE TERMINAL? Truecolor (24bit colors) are enabled by default if a supporting terminal is detected. If your terminal is not detected but you are sure it supports @@ -45,7 +49,8 @@ truecolor, add this to your |init.vim|: set termguicolors < -NVIM SHOWS WEIRD SYMBOLS (`�[2 q`) WHEN CHANGING MODES ~ +------------------------------------------------------------------------------ +NVIM SHOWS WEIRD SYMBOLS (`�[2 q`) WHEN CHANGING MODES This is a bug in your terminal emulator. It happens because Nvim sends cursor-shape termcodes by default, if the terminal appears to be @@ -63,7 +68,8 @@ To workaround the issue, you can: See also |$TERM| for recommended values of `$TERM`. -HOW TO CHANGE CURSOR SHAPE IN THE TERMINAL? ~ +------------------------------------------------------------------------------ +HOW TO CHANGE CURSOR SHAPE IN THE TERMINAL? - For Nvim 0.1.7 or older: see the note about `NVIM_TUI_ENABLE_CURSOR_SHAPE` in `man nvim`. - For Nvim 0.2 or newer: cursor styling is controlled by the 'guicursor' option. @@ -83,7 +89,8 @@ HOW TO CHANGE CURSOR SHAPE IN THE TERMINAL? ~ https://github.com/neovim/neovim/issues/2537 -HOW TO CHANGE CURSOR COLOR IN THE TERMINAL? ~ +------------------------------------------------------------------------------ +HOW TO CHANGE CURSOR COLOR IN THE TERMINAL? Cursor styling (shape, color, behavior) is controlled by 'guicursor', even in the terminal. Cursor color (as opposed to shape) only works if @@ -98,7 +105,8 @@ which sets different colors in insert-mode and normal-mode: :set guicursor=n-v-c:block-Cursor/lCursor,i-ci-ve:ver25-Cursor2/lCursor2,r-cr:hor20,o:hor50 < -CURSOR STYLE ISN'T RESTORED AFTER EXITING OR SUSPENDING AND RESUMING NVIM ~ +------------------------------------------------------------------------------ +CURSOR STYLE ISN'T RESTORED AFTER EXITING OR SUSPENDING AND RESUMING NVIM Terminals do not provide a way to query the cursor style. Use autocommands to manage the cursor style: @@ -110,14 +118,16 @@ manage the cursor style: au VimLeave,VimSuspend * set guicursor=a:block-blinkon0 < -CURSOR SHAPE DOESN'T CHANGE IN TMUX ~ +------------------------------------------------------------------------------ +CURSOR SHAPE DOESN'T CHANGE IN TMUX tmux decides that, not Nvim. See |tui-cursor-shape| for a fix. See #3165 https://github.com/neovim/neovim/pull/3165 for discussion. -CURSOR FLICKER IN TMUX? ~ +------------------------------------------------------------------------------ +CURSOR FLICKER IN TMUX? If cursor `_` appears and disappears very quickly when opening nvim without a document under tmux, and you set |ctermbg| in `EndOfBuffer` and `Normal`, try @@ -127,7 +137,8 @@ setting these to `NONE`: hi Normal ctermbg=NONE ctermfg=200 cterm=NONE < -WHAT HAPPENED TO --remote AND FRIENDS? ~ +------------------------------------------------------------------------------ +WHAT HAPPENED TO --remote AND FRIENDS? |--remote| is partly supported. |clientserver| @@ -138,7 +149,8 @@ https://github.com/mhinz/neovim-remote instead. Runtime issues *faq-runtime* -COPYING TO X11 PRIMARY SELECTION WITH THE MOUSE DOESN'T WORK ~ +------------------------------------------------------------------------------ +COPYING TO X11 PRIMARY SELECTION WITH THE MOUSE DOESN'T WORK `clipboard=autoselect` is not implemented yet https://github.com/neovim/neovim/issues/2325. You may find this workaround to @@ -148,7 +160,8 @@ be useful: vnoremap <2-LeftRelease> "*ygv < -MY CTRL-H MAPPING DOESN'T WORK ~ +------------------------------------------------------------------------------ +MY CTRL-H MAPPING DOESN'T WORK This was fixed in Nvim 0.2. If you are running Nvim 0.1.7 or older, adjust your terminal's "kbs" (key_backspace) terminfo entry: @@ -160,7 +173,8 @@ adjust your terminal's "kbs" (key_backspace) terminfo entry: commands). -<HOME> OR SOME OTHER "SPECIAL" KEY DOESN'T WORK ~ +------------------------------------------------------------------------------ +<HOME> OR SOME OTHER "SPECIAL" KEY DOESN'T WORK Make sure |$TERM| is set correctly. @@ -169,7 +183,8 @@ Make sure |$TERM| is set correctly. Try `TERM=xterm-256color`. -:! AND SYSTEM() DO WEIRD THINGS WITH INTERACTIVE PROCESSES ~ +------------------------------------------------------------------------------ +:! AND SYSTEM() DO WEIRD THINGS WITH INTERACTIVE PROCESSES Interactive commands are supported by |:terminal| in Nvim. But |:!| and |system()| do not support interactive commands, primarily because Nvim UIs use @@ -181,7 +196,8 @@ See also #1496 https://github.com/neovim/neovim/issues/1496 and #8217 https://github.com/neovim/neovim/issues/8217#issuecomment-402152307. -PYTHON SUPPORT ISN'T WORKING ~ +------------------------------------------------------------------------------ +PYTHON SUPPORT ISN'T WORKING Run |:checkhealth| in Nvim for automatic diagnosis. @@ -202,7 +218,8 @@ Other hints: - The python `neovim` module was renamed to `pynvim` (long ago). -:CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME ~ +------------------------------------------------------------------------------ +:CHECKHEALTH REPORTS E5009: INVALID $VIMRUNTIME This means |$VIMRUNTIME| or 'runtimepath' is broken. @@ -210,7 +227,8 @@ This means |$VIMRUNTIME| or 'runtimepath' is broken. - The |$VIMRUNTIME| directory contents should be readable by the current user. - Verify that `:echo &runtimepath` contains the $VIMRUNTIME path. -NEOVIM CAN'T FIND ITS RUNTIME ~ +------------------------------------------------------------------------------ +NEOVIM CAN'T FIND ITS RUNTIME This is the case if `:help nvim` shows `E149: Sorry, no help for nvim`. @@ -223,7 +241,8 @@ Also make sure that you don't accidentally overwrite your runtimepath 'runtimepath'). -NEOVIM IS SLOW ~ +------------------------------------------------------------------------------ +NEOVIM IS SLOW Use a fast terminal emulator: @@ -244,7 +263,8 @@ If it reports `Build type: Debug` and you're building Nvim from source, see https://github.com/neovim/neovim/blob/master/BUILD.md. -COLORS AREN'T DISPLAYED CORRECTLY ~ +------------------------------------------------------------------------------ +COLORS AREN'T DISPLAYED CORRECTLY Ensure that |$TERM| is set correctly. @@ -268,7 +288,8 @@ For GNU `screen`, configure your `.screenrc` NOTE: Nvim ignores `t_Co` and other |t_xx| terminal codes. -NEOVIM CAN'T READ UTF-8 CHARACTERS ~ +------------------------------------------------------------------------------ +NEOVIM CAN'T READ UTF-8 CHARACTERS Run the following from the command line: >bash @@ -280,7 +301,8 @@ If there's no results, you might not be using a UTF-8 locale. See these issues: - https://github.com/neovim/neovim/issues/2386 -ESC IN TMUX OR GNU SCREEN IS DELAYED ~ +------------------------------------------------------------------------------ +ESC IN TMUX OR GNU SCREEN IS DELAYED This is a common problem https://www.google.com/?q=tmux%20vim%20escape%20delay in `tmux` / `screen` @@ -311,7 +333,8 @@ Nvim 0.3 mimics the Vim behavior while still fully supporting ALT mappings. See |i_ALT|. -ESC IN GNU SCREEN IS LOST WHEN MOUSE MODE IS ENABLED ~ +------------------------------------------------------------------------------ +ESC IN GNU SCREEN IS LOST WHEN MOUSE MODE IS ENABLED This happens because of a bug in screen https://savannah.gnu.org/bugs/?60196 : in mouse mode, screen assumes that `ESC` is part of a mouse sequence and will @@ -321,7 +344,8 @@ this other than double-pressing escape, which causes a single escape to be passed through to Nvim. -CALLING INPUTLIST(), ECHOMSG, ... IN FILETYPE PLUGINS AND AUTOCMD DOES NOT WORK ~ +------------------------------------------------------------------------------ +CALLING INPUTLIST(), ECHOMSG, ... IN FILETYPE PLUGINS AND AUTOCMD DOES NOT WORK - https://github.com/neovim/neovim/issues/10008 - https://github.com/neovim/neovim/issues/10116 @@ -336,7 +360,8 @@ workaround, use `set shortmess-=F` or use `unsilent` as follows. autocmd BufNewFile * unsilent echomsg 'The autocmd has been fired.' < -G:CLIPBOARD SETTINGS ARE NOT USED. ~ +------------------------------------------------------------------------------ +G:CLIPBOARD SETTINGS ARE NOT USED. If the clipboard provider is already loaded, you will need to reload it after configuration. Use the following configuration. @@ -368,19 +393,22 @@ Or, if you want automatic reloading when assigning to |g:clipboard|, set Build issues *faq-build* -GENERAL BUILD ISSUES ~ +------------------------------------------------------------------------------ +GENERAL BUILD ISSUES Run `make distclean && make` to rule out a stale build environment causing the failure. -SETTINGS IN LOCAL.MK DON'T TAKE EFFECT ~ +------------------------------------------------------------------------------ +SETTINGS IN LOCAL.MK DON'T TAKE EFFECT CMake caches build settings, so you might need to run `rm -r build && make` after modifying `local.mk`. -CMAKE ERRORS ~ +------------------------------------------------------------------------------ +CMAKE ERRORS `configure_file Problem configuring file` @@ -389,7 +417,8 @@ root user, then later run an unprivileged `make`. To fix this, run `rm -rf build` and try again. -GENERATING HELPTAGS FAILED ~ +------------------------------------------------------------------------------ +GENERATING HELPTAGS FAILED If re-installation fails with "Generating helptags failed", try removing the previously installed runtime directory (if `CMAKE_INSTALL_PREFIX` is not set @@ -402,13 +431,15 @@ during building, the default is `/usr/local/share/nvim`): Design *faq-design* -WHY NOT USE JSON FOR RPC? ~ +------------------------------------------------------------------------------ +WHY NOT USE JSON FOR RPC? - JSON cannot easily/efficiently handle binary data - JSON specification is ambiguous: https://seriot.ch/parsing_json.php -WHY EMBED LUA INSTEAD OF X? ~ +------------------------------------------------------------------------------ +WHY EMBED LUA INSTEAD OF X? - Lua is a very small language, ideal for embedding. The biggest advantage of Python/Ruby/etc is their huge collection of libraries, but that isn't @@ -435,7 +466,8 @@ See also: - Discussion Python embedding https://lobste.rs/s/pnuak4/mercurial_s_journey_reflections_on#c_zshdwy -WHY LUA 5.1 INSTEAD OF LUA 5.3+? ~ +------------------------------------------------------------------------------ +WHY LUA 5.1 INSTEAD OF LUA 5.3+? Lua 5.1 is a different language than 5.3. The Lua org makes breaking changes with every new version, so even if we switched (not upgraded, but switched) to @@ -447,12 +479,14 @@ Nvim itself already is a pretty good "stdlib" for Lua, and we will continue to grow and enhance it. Changing the rules of Lua gains nothing in this context. -WILL NEOVIM TRANSLATE VIMSCRIPT TO LUA, INSTEAD OF EXECUTING VIMSCRIPT DIRECTLY? ~ +------------------------------------------------------------------------------ +WILL NEOVIM TRANSLATE VIMSCRIPT TO LUA, INSTEAD OF EXECUTING VIMSCRIPT DIRECTLY? We have no plans for transpiling Vimscript. It was explored in https://github.com/tjdevries/vim9jit -ARE PLUGIN AUTHORS ENCOURAGED TO PORT THEIR PLUGINS FROM VIMSCRIPT TO LUA? DO YOU PLAN ON SUPPORTING VIMSCRIPT INDEFINITELY? (#1152) ~ +------------------------------------------------------------------------------ +ARE PLUGIN AUTHORS ENCOURAGED TO PORT THEIR PLUGINS FROM VIMSCRIPT TO LUA? DO YOU PLAN ON SUPPORTING VIMSCRIPT INDEFINITELY? We don't anticipate any reason to deprecate Vimscript, which is a valuable DSL https://en.wikipedia.org/wiki/Domain-specific_language for text-editing tasks. diff --git a/runtime/doc/lsp.txt b/runtime/doc/lsp.txt @@ -30,7 +30,7 @@ Follow these steps to get LSP features: 2. Define a new config |lsp-new-config| (or install https://github.com/neovim/nvim-lspconfig). Example: >lua - vim.lsp.config['luals'] = { + vim.lsp.config['lua_ls'] = { -- Command and arguments to start the server. cmd = { 'lua-language-server' }, -- Filetypes to automatically attach to. @@ -52,7 +52,7 @@ Follow these steps to get LSP features: 3. Use |vim.lsp.enable()| to enable the config. Example: >lua - vim.lsp.enable('luals') + vim.lsp.enable('lua_ls') < 4. Open a code file matching one of the `filetypes` specified in the config. Note: Depending on the LSP server, you may need to ensure your project has @@ -997,8 +997,8 @@ config({name}, {cfg}) *vim.lsp.config()* filetypes = { 'c', 'cpp' }, } < - • Get the resolved configuration for "luals": >lua - local cfg = vim.lsp.config.luals + • Get the resolved configuration for "lua_ls": >lua + local cfg = vim.lsp.config.lua_ls < Attributes: ~ @@ -1014,7 +1014,7 @@ enable({name}, {enable}) *vim.lsp.enable()* Examples: >lua vim.lsp.enable('clangd') - vim.lsp.enable({'luals', 'pyright'}) + vim.lsp.enable({'lua_ls', 'pyright'}) < Example: *lsp-restart* Passing `false` stops and detaches the client(s). @@ -1689,12 +1689,12 @@ Lua module: vim.lsp.client *lsp-client* Fields: ~ • {before_init}? (`fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig)`) - Callback invoked before the LSP "initialize" - phase, where `params` contains the parameters - being sent to the server and `config` is the - config that was passed to |vim.lsp.start()|. - You can use this to modify parameters before - they are sent. + Callback which can modify parameters before + they are sent to the server. Invoked before LSP + "initialize" phase (after `cmd` is invoked), + where `params` is the parameters being sent to + the server and `config` is the config passed to + |vim.lsp.start()|. • {capabilities}? (`lsp.ClientCapabilities`) Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, diff --git a/runtime/doc/lua-plugin.txt b/runtime/doc/lua-plugin.txt @@ -217,6 +217,11 @@ Consider making use of 'filetype' for any functionality that is specific to a filetype, by putting the initialization logic in a `ftplugin/{filetype}.lua` script. +For buffers owned by your plugin (often used to show a custom UI or view), +typically your plugin will set a custom 'filetype'. In that case, it's useful +to set the 'filetype' "as late as possible", so that users can override +buffer-local settings after your plugin has (re)initialized the buffer. + FILETYPE EXAMPLE A plugin tailored to Rust development might have initialization in @@ -240,6 +245,10 @@ A plugin tailored to Rust development might have initialization in ============================================================================== Configuration *lua-plugin-config* +To allow users to override buffer-local configuration for filetypes owned by +your plugin, publish a |FileType| event, "as late as possible". +|lua-plugin-filetype| + Once you have merged the default configuration with the user's config, you should validate configs. @@ -251,6 +260,32 @@ Validations could include: check, to reduce overhead. ============================================================================== +UI *lua-plugin-ui* + +Some plugins have their own "UI" which they present in a buffer that the +plugin "owns". In that buffer typically you will want to provide custom +actions. + +Besides creating |<Plug>| mappings, you may want to consider providing actions +by defining an in-process LSP server. Offering actions as code-actions +|vim.lsp.buf.code_action()| means the user can see all available actions using +the default |gra| mapping to view the code-actions menu. They can even define +mappings to a specific action by invoking `vim.lsp.buf.code_action()` with the +`filter` + `apply` parameters: >lua + + vim.lsp.buf.code_action({ + apply = true, + filter = function(a) + return a.title == 'Do something' + end, + }) +< + +Example: See `runtime/lua/vim/pack/_lsp.lua` for how vim.pack defines an +in-process LSP server to provide interactive features in its `nvim-pack://` +buffer. + +============================================================================== Troubleshooting *lua-plugin-troubleshooting* While developing a plugin, you can use the |:restart| command to see the diff --git a/runtime/doc/lua.txt b/runtime/doc/lua.txt @@ -1710,7 +1710,7 @@ vim.is_callable({f}) *vim.is_callable()* Returns true if object `f` can be called as a function. Parameters: ~ - • {f} (`any`) Any object + • {f} (`any?`) Any object Return: ~ (`boolean`) `true` if `f` is callable, else `false` @@ -1727,7 +1727,7 @@ vim.isarray({t}) *vim.isarray()* |rpcrequest()| or |vim.fn|. Parameters: ~ - • {t} (`table?`) + • {t} (`any?`) Return: ~ (`boolean`) `true` if array-like table, else `false`. diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt @@ -80,7 +80,6 @@ EVENTS message UI that mimics the legacy message grid. Benefit: reduced UI event traffic and more flexibility for UIs. The `msg_history_show` event has an additional "prev_cmd" argument. -• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message. HIGHLIGHTS @@ -88,12 +87,11 @@ HIGHLIGHTS LSP -• `root_markers` in |vim.lsp.Config| can now be ordered by priority. +• JSON "null" values in LSP messages are represented as |vim.NIL| instead of `nil`. + Missing fields (as opposed to JSON "null") are still represented as `nil`. • The function set with |vim.lsp.log.set_format_func()| is now given all arguments corresponding to a log entry instead of the individual arguments. -• `vim.lsp.semantic_tokens.start/stop` now renamed to - `vim.lsp.semantic_tokens.enable` -• Missing fields in LSP messages are now represented using |vim.NIL| instead of nil. +• Renamed `vim.lsp.semantic_tokens` `start()/stop()` to `enable()`. • |vim.lsp.util.convert_signature_help_to_markdown_lines()| activeParameter handling updated: • Values < 0 are now treated as `nil` instead of 0. @@ -103,7 +101,6 @@ LSP LUA • Renamed `vim.diff` to `vim.text.diff`. -• |vim.net.request()| adds a minimal HTTP GET API using curl. OPTIONS @@ -120,7 +117,6 @@ TREESITTER `metadata[capture_id].offset`. The offset will be applied in |vim.treesitter.get_range()|, which should be preferred over reading metadata directly for retrieving node ranges. -• |Query:iter_captures()| supports specifying starting and ending columns. TUI @@ -203,6 +199,7 @@ EDITOR EVENTS +• A new `empty` message kind is emitted for an empty (e.g. `:echo ""`) message. • |CmdlineLeave| sets |v:char| to the character that stops the Cmdline mode. • |CmdlineLeavePre| triggered before preparing to leave the command line. • New `append` parameter for |ui-messages| `msg_show` event. @@ -220,7 +217,7 @@ HIGHLIGHTS LSP • |vim.lsp.ClientConfig| gained `workspace_required`. -• You can control priority of |vim.lsp.Config| `root_markers`. +• You can control the priority of |vim.lsp.Config| `root_markers`. • Support for `textDocument/documentColor`: |lsp-document_color| https://microsoft.github.io/language-server-protocol/specification/#textDocument_documentColor • Support for `textDocument/colorPresentation |lsp-document_color| @@ -261,6 +258,7 @@ LSP LUA +• |vim.net.request()| can fetch files via HTTP GET requests. • |vim.wait()| returns the callback results. • Lua type annotations for `vim.uv`. • |vim.hl.range()| now allows multiple timed highlights. @@ -330,6 +328,7 @@ TERMINAL TREESITTER +• |Query:iter_captures()| supports specifying starting and ending columns. • |:EditQuery| command gained tab-completion, works with injected languages. TUI diff --git a/runtime/lua/vim/lsp.lua b/runtime/lua/vim/lsp.lua @@ -301,9 +301,9 @@ end --- filetypes = { 'c', 'cpp' }, --- } --- ``` ---- - Get the resolved configuration for "luals": +--- - Get the resolved configuration for "lua_ls": --- ```lua ---- local cfg = vim.lsp.config.luals +--- local cfg = vim.lsp.config.lua_ls --- ``` --- ---@since 13 @@ -522,7 +522,7 @@ end --- --- ```lua --- vim.lsp.enable('clangd') ---- vim.lsp.enable({'luals', 'pyright'}) +--- vim.lsp.enable({'lua_ls', 'pyright'}) --- ``` --- --- Example: [lsp-restart]() Passing `false` stops and detaches the client(s). Thus you can diff --git a/runtime/lua/vim/lsp/client.lua b/runtime/lua/vim/lsp/client.lua @@ -34,9 +34,9 @@ local all_clients = {} --- @class vim.lsp.ClientConfig --- ---- Callback invoked before the LSP "initialize" phase, where `params` contains the parameters ---- being sent to the server and `config` is the config that was passed to |vim.lsp.start()|. ---- You can use this to modify parameters before they are sent. +--- Callback which can modify parameters before they are sent to the server. Invoked before LSP +--- "initialize" phase (after `cmd` is invoked), where `params` is the parameters being sent to the +--- server and `config` is the config passed to |vim.lsp.start()|. --- @field before_init? fun(params: lsp.InitializeParams, config: vim.lsp.ClientConfig) --- --- Map overriding the default capabilities defined by |vim.lsp.protocol.make_client_capabilities()|, diff --git a/runtime/lua/vim/shared.lua b/runtime/lua/vim/shared.lua @@ -826,7 +826,7 @@ end --- ---@see https://github.com/openresty/luajit2#tableisarray --- ----@param t? table +---@param t? any ---@return boolean `true` if array-like table, else `false`. function vim.isarray(t) if type(t) ~= 'table' then @@ -1246,7 +1246,7 @@ end --- Returns true if object `f` can be called as a function. --- ----@param f any Any object +---@param f? any Any object ---@return boolean `true` if `f` is callable, else `false` function vim.is_callable(f) if type(f) == 'function' then diff --git a/runtime/lua/vim/treesitter/_headings.lua b/runtime/lua/vim/treesitter/_headings.lua @@ -95,7 +95,7 @@ function M.show_toc(qf_height) -- add indentation for nicer list formatting for _, heading in pairs(headings) do -- Quickfix trims whitespace, so use non-breaking space instead - heading.text = ('\194\160'):rep(heading.level - 1) .. heading.text + heading.text = ('\194\160'):rep((heading.level - 1) * 2) .. heading.text end vim.fn.setloclist(0, headings, ' ') vim.fn.setloclist(0, {}, 'a', { title = 'Table of contents' }) diff --git a/src/gen/gen_help_html.lua b/src/gen/gen_help_html.lua @@ -91,6 +91,7 @@ local new_layout = { local redirects = { ['api-ui-events'] = 'ui', ['credits'] = 'backers', + ['dev_tools'] = 'debug', ['plugins'] = 'editorconfig', ['terminal'] = 'nvim_terminal_emulator', ['tui'] = 'term', diff --git a/src/nvim/README.md b/src/nvim/README.md @@ -1,526 +1,5 @@ -Nvim core -========= +## Moved to: -Module-specific details are documented at the top of each module (`terminal.c`, `undo.c`, …). - -See `:help dev` for guidelines. - -Filename conventions --------------------- - -The source files use extensions to hint about their purpose. - -- `*.c`, `*.generated.c` - full C files, with all includes, etc. -- `*.c.h` - parametrized C files, contain all necessary includes, but require - defining macros before actually using. Example: `typval_encode.c.h` -- `*.h` - full headers, with all includes. Does *not* apply to `*.generated.h`. -- `*.h.generated.h` - exported functions’ declarations. -- `*.c.generated.h` - static functions’ declarations. - -Common structures ------------------ - -- StringBuilder -- kvec or garray.c for dynamic lists / vectors (use StringBuilder for strings) - -Logs ----- - -Low-level log messages sink to `$NVIM_LOG_FILE`. - -UI events are logged at DEBUG level. - - rm -rf build/ - make CMAKE_EXTRA_FLAGS="-DLOG_DEBUG" - -Use `LOG_CALLSTACK()` (Linux only) to log the current stacktrace. To log to an -alternate file (e.g. stderr) use `LOG_CALLSTACK_TO_FILE(FILE*)`. Requires -`-no-pie` ([ref](https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=860394#15)): - - rm -rf build/ - make CMAKE_EXTRA_FLAGS="-DLOG_DEBUG -DCMAKE_C_FLAGS=-no-pie" - -Many log messages have a shared prefix, such as "UI" or "RPC". Use the shell to -filter the log, e.g. at DEBUG level you might want to exclude UI messages: - - tail -F ~/.local/state/nvim/log | cat -v | stdbuf -o0 grep -v UI | stdbuf -o0 tee -a log - -Build with ASAN ---------------- - -Building Nvim with Clang sanitizers (Address Sanitizer: ASan, Undefined -Behavior Sanitizer: UBSan, Memory Sanitizer: MSan, Thread Sanitizer: TSan) is -a good way to catch undefined behavior, leaks and other errors as soon as they -happen. It's significantly faster than Valgrind. - -Requires clang 3.4 or later, and `llvm-symbolizer` must be in `$PATH`: - - clang --version - -Build Nvim with sanitizer instrumentation (choose one): - - CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_ASAN_UBSAN=ON" - CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_MSAN=ON" - CC=clang make CMAKE_EXTRA_FLAGS="-DENABLE_TSAN=ON" - -Create a directory to store logs: - - mkdir -p "$HOME/logs" - -Configure the sanitizer(s) via these environment variables: - - # Change to detect_leaks=1 to detect memory leaks (slower, noisier). - export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan" - # Show backtraces in the logs. - export MSAN_OPTIONS="log_path=${HOME}/logs/msan" - export TSAN_OPTIONS="log_path=${HOME}/logs/tsan" - -Logs will be written to `${HOME}/logs/*san.PID` then. - -For more information: https://github.com/google/sanitizers/wiki/SanitizerCommonFlags - -Reproducible build ------------------- - -To make a reproducible build of Nvim, set cmake variable `LUA_GEN_PRG` to -a LuaJIT binary built with `LUAJIT_SECURITY_PRN=0`. See commit -cb757f2663e6950e655c6306d713338dfa66b18d. - -Debug: Performance ------------------- - -### Profiling (easy) - -For debugging performance bottlenecks in any code, there is a simple (and very -effective) approach: - -1. Run the slow code in a loop. -2. Break execution ~5 times and save the stacktrace. -3. The cause of the bottleneck will (almost always) appear in most of the stacktraces. - -### Profiling (fancy) - -For more advanced profiling, consider `perf` + `flamegraph`. - -### USDT profiling (powerful) - -Or you can use USDT probes via `NVIM_PROBE` ([#12036](https://github.com/neovim/neovim/pull/12036)). - -> USDT is basically a way to define stable probe points in userland binaries. -> The benefit of bcc is the ability to define logic to go along with the probe -> points. - -Tools: -- bpftrace provides an awk-like language to the kernel bytecode, BPF. -- BCC provides a subset of C. Provides more complex logic than bpftrace, but takes a bit more effort. - -Example using bpftrace to track slow vim functions, and print out any files -that were opened during the trace. At the end, it prints a histogram of -function timing: - - #!/usr/bin/env bpftrace - - BEGIN { - @depth = -1; - } - - tracepoint:sched:sched_process_fork /@pidmap[args->parent_pid]/ { - @pidmap[args->child_pid] = 1; - } - - tracepoint:sched:sched_process_exit /@pidmap[args->pid]/ { - delete(@pidmap[args->pid]); - } - - usdt:build/bin/nvim:neovim:eval__call_func__entry { - @pidmap[pid] = 1; - @depth++; - @funcentry[@depth] = nsecs; - } - - usdt:build/bin/nvim:neovim:eval__call_func__return { - $func = str(arg0); - $msecs = (nsecs - @funcentry[@depth]) / 1000000; - - @time_histo = hist($msecs); - - if ($msecs >= 1000) { - printf("%u ms for %s\n", $msecs, $func); - print(@files); - } - - clear(@files); - delete(@funcentry[@depth]); - @depth--; - } - - tracepoint:syscalls:sys_enter_open, - tracepoint:syscalls:sys_enter_openat { - if (@pidmap[pid] == 1 && @depth >= 0) { - @files[str(args->filename)] = count(); - } - } - - END { - clear(@depth); - } - - $ sudo bpftrace funcslower.bt - 1527 ms for Slower - @files[/usr/lib/libstdc++.so.6]: 2 - @files[/etc/fish/config.fish]: 2 - <snip> - - ^C - @time_histo: - [0] 71430 |@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@| - [1] 346 | | - [2, 4) 208 | | - [4, 8) 91 | | - [8, 16) 22 | | - [16, 32) 85 | | - [32, 64) 7 | | - [64, 128) 0 | | - [128, 256) 0 | | - [256, 512) 6 | | - [512, 1K) 1 | | - [1K, 2K) 5 | | - -Debug: TUI ----------- - -### TUI troubleshoot - -Nvim logs its internal terminfo state at 'verbose' level 3. This makes it -possible to see exactly what terminfo values Nvim is using on any system. - - nvim -V3log - -### TUI Debugging with gdb/lldb - -Launching the nvim TUI involves two processes, one for main editor state and one -for rendering the TUI. Both of these processes use the nvim binary, so somewhat -confusingly setting a breakpoint in either will generally succeed but may not be -hit depending on which process the breakpoints were set in. - -To debug the main process, you can debug the nvim binary with the `--headless` -flag which does not launch the TUI and will allow you to set breakpoints in code -not related to TUI rendering like so: - - lldb -- ./build/bin/nvim --headless --listen ~/.cache/nvim/debug-server.pipe - -While in lldb, enter `run`. You can then attach to the headless process in a -new terminal window to interact with the editor like so: - - ./build/bin/nvim --remote-ui --server ~/.cache/nvim/debug-server.pipe - -Conversely for debugging TUI rendering, you can start a headless process and -debug the remote-ui process multiple times without losing editor state. - -For details on using nvim-dap and automatically debugging the child (main) -process, see -[here](https://zignar.net/2023/02/17/debugging-neovim-with-neovim-and-nvim-dap/) - -### TUI trace - -The ancient `script` command is still the "state of the art" for tracing -terminal behavior. The libvterm `vterm-dump` utility formats the result for -human-readability. - -Record a Nvim terminal session and format it with `vterm-dump`: - - script foo - ./build/bin/nvim -u NONE - # Exit the script session with CTRL-d - - # Use `vterm-dump` utility to format the result. - ./.deps/usr/bin/vterm-dump foo > bar - -Then you can compare `bar` with another session, to debug TUI behavior. - -### TUI redraw - -Set the 'writedelay' and 'redrawdebug' options to see where and when the UI is painted. - - :set writedelay=50 rdb=compositor - -Note: neovim uses an internal screenbuffer to only send minimal updates even if a large -region is repainted internally. To also highlight excess internal redraws, use - - :set writedelay=50 rdb=compositor,nodelta - -### Terminal reference - -- `man terminfo` -- http://bazaar.launchpad.net/~libvterm/libvterm/trunk/view/head:/doc/seqs.txt -- http://invisible-island.net/xterm/ctlseqs/ctlseqs.html - -Data structures ---------------- - -Buffer text is stored as a tree of line segments, defined in [memline.c](https://github.com/neovim/neovim/blob/v0.9.5/src/nvim/memline.c#L8-L35). -The central idea is found in [ml_find_line](https://github.com/neovim/neovim/blob/v0.9.5/src/nvim/memline.c#L2800). - -Nvim lifecycle --------------- - -Following describes how Nvim processes input. - -Consider a typical Vim-like editing session: - -01. Vim displays the welcome screen -02. User types: `:` -03. Vim enters command-line mode -04. User types: `edit README.txt<CR>` -05. Vim opens the file and returns to normal mode -06. User types: `G` -07. Vim navigates to the end of the file -09. User types: `5` -10. Vim enters count-pending mode -11. User types: `d` -12. Vim enters operator-pending mode -13. User types: `w` -14. Vim deletes 5 words -15. User types: `g` -16. Vim enters the "g command mode" -17. User types: `g` -18. Vim goes to the beginning of the file -19. User types: `i` -20. Vim enters insert mode -21. User types: `word<ESC>` -22. Vim inserts "word" at the beginning and returns to normal mode - -Note that we split user actions into sequences of inputs that change the state -of the editor. While there's no documentation about a "g command mode" (step -16), internally it is implemented similarly to "operator-pending mode". - -From this we can see that Vim has the behavior of an input-driven state machine -(more specifically, a pushdown automaton since it requires a stack for -transitioning back from states). Assuming each state has a callback responsible -for handling keys, this pseudocode represents the main program loop: - -```py -def state_enter(state_callback, data): - do - key = readkey() # read a key from the user - while state_callback(data, key) # invoke the callback for the current state -``` - -That is, each state is entered by calling `state_enter` and passing a -state-specific callback and data. Here is a high-level pseudocode for a program -that implements something like the workflow described above: - -```py -def main() - state_enter(normal_state, {}): - -def normal_state(data, key): - if key == ':': - state_enter(command_line_state, {}) - elif key == 'i': - state_enter(insert_state, {}) - elif key == 'd': - state_enter(delete_operator_state, {}) - elif key == 'g': - state_enter(g_command_state, {}) - elif is_number(key): - state_enter(get_operator_count_state, {'count': key}) - elif key == 'G' - jump_to_eof() - return true - -def command_line_state(data, key): - if key == '<cr>': - if data['input']: - execute_ex_command(data['input']) - return false - elif key == '<esc>' - return false - - if not data['input']: - data['input'] = '' - - data['input'] += key - return true - -def delete_operator_state(data, key): - count = data['count'] or 1 - if key == 'w': - delete_word(count) - elif key == '$': - delete_to_eol(count) - return false # return to normal mode - -def g_command_state(data, key): - if key == 'g': - go_top() - elif key == 'v': - reselect() - return false # return to normal mode - -def get_operator_count_state(data, key): - if is_number(key): - data['count'] += key - return true - unshift_key(key) # return key to the input buffer - state_enter(delete_operator_state, data) - return false - -def insert_state(data, key): - if key == '<esc>': - return false # exit insert mode - self_insert(key) - return true -``` - -The above gives an idea of how Nvim is organized internally. Some states like -the `g_command_state` or `get_operator_count_state` do not have a dedicated -`state_enter` callback, but are implicitly embedded into other states (this -will change later as we continue the refactoring effort). To start reading the -actual code, here's the recommended order: - -1. `state_enter()` function (state.c). This is the actual program loop, - note that a `VimState` structure is used, which contains function pointers - for the callback and state data. -2. `main()` function (main.c). After all startup, `normal_enter` is called - at the end of function to enter normal mode. -3. `normal_enter()` function (normal.c) is a small wrapper for setting - up the NormalState structure and calling `state_enter`. -4. `normal_check()` function (normal.c) is called before each iteration of - normal mode. -5. `normal_execute()` function (normal.c) is called when a key is read in normal - mode. - -The basic structure described for normal mode in 3, 4 and 5 is used for other -modes managed by the `state_enter` loop: - -- command-line mode: `command_line_{enter,check,execute}()`(`ex_getln.c`) -- insert mode: `insert_{enter,check,execute}()`(`edit.c`) -- terminal mode: `terminal_{enter,execute}()`(`terminal.c`) - -## Important variables - -The current mode is stored in `State`. The values it can have are `MODE_NORMAL`, -`MODE_INSERT`, `MODE_CMDLINE`, and a few others. - -The current window is `curwin`. The current buffer is `curbuf`. These point -to structures with the cursor position in the window, option values, the file -name, etc. - -All the global variables are declared in `globals.h`. - -### The main event-loop - -The main loop is implemented in state_enter. The basic idea is that Vim waits -for the user to type a character and processes it until another character is -needed. Thus there are several places where Vim waits for a character to be -typed. The `vgetc()` function is used for this. It also handles mapping. - -What we consider the "Nvim event loop" is actually a wrapper around `uv_run` to -handle both the `fast_events` queue and possibly (a suitable subset of) deferred -events. Therefore "raw" `vim.uv.run()` is often not enough to "yield" from Lua -plugins; instead they can call `vim.wait(0)`. - -Updating the screen is mostly postponed until a command or a sequence of -commands has finished. The work is done by `update_screen()`, which calls -`win_update()` for every window, which calls `win_line()` for every line. -See the start of [drawscreen.c](drawscreen.c) for more explanations. - -### Command-line mode - -When typing a `:`, `normal_cmd()` will call `getcmdline()` to obtain a line with -an Ex command. `getcmdline()` calls a loop that will handle each typed -character. It returns when hitting `<CR>` or `<Esc>` or some other character that -ends the command line mode. - -### Ex commands - -Ex commands are handled by the function `do_cmdline()`. It does the generic -parsing of the `:` command line and calls `do_one_cmd()` for each separate -command. It also takes care of while loops. - -`do_one_cmd()` parses the range and generic arguments and puts them in the -exarg_t and passes it to the function that handles the command. - -The `:` commands are listed in [ex_cmds.lua](ex_cmds.lua). - -### Normal mode commands - -The Normal mode commands are handled by the `normal_cmd()` function. It also -handles the optional count and an extra character for some commands. These -are passed in a `cmdarg_T` to the function that handles the command. - -There is a table `nv_cmds` in [normal.c](normal.c) which -lists the first character of every -command. The second entry of each item is the name of the function that -handles the command. - -### Insert mode commands - -When doing an `i` or `a` command, `normal_cmd()` will call the `edit()` function. -It contains a loop that waits for the next character and handles it. It -returns when leaving Insert mode. - -### Options - -There is a list with all option names in [options.lua](options.lua). - -Async event support -------------------- - -One of the features Nvim added is the support for handling arbitrary -asynchronous events, which can include: - -- RPC requests -- job control callbacks -- timers - -Nvim implements this functionality by entering another event loop while -waiting for characters, so instead of: - -```py -def state_enter(state_callback, data): - do - key = readkey() # read a key from the user - while state_callback(data, key) # invoke the callback for the current state -``` - -Nvim program loop is more like: - -```py -def state_enter(state_callback, data): - do - event = read_next_event() # read an event from the operating system - while state_callback(data, event) # invoke the callback for the current state -``` - -where `event` is something the operating system delivers to us, including (but -not limited to) user input. The `read_next_event()` part is internally -implemented by libuv, the platform layer used by Nvim. - -Since Nvim inherited its code from Vim, the states are not prepared to receive -"arbitrary events", so we use a special key to represent those (When a state -receives an "arbitrary event", it normally doesn't do anything other than -update the screen). - -Main loop ---------- - -The `Loop` structure (which describes `main_loop`) abstracts multiple queues -into one loop: - - uv_loop_t uv; - MultiQueue *events; - MultiQueue *thread_events; - MultiQueue *fast_events; - -`loop_poll_events` checks `Loop.uv` and `Loop.fast_events` whenever Nvim is -idle, and also at `os_breakcheck` intervals. - -MultiQueue is cool because you can attach throw-away "child queues" trivially. -For example `do_os_system()` does this (for every spawned process!) to -automatically route events onto the `main_loop`: - - Process *proc = &uvproc.process; - MultiQueue *events = multiqueue_new_child(main_loop.events); - proc->events = events; +- [dev_arch.txt](../../runtime/doc/dev_arch.txt) +- [dev_tools.txt](../../runtime/doc/dev_tools.txt) +- [develop.txt](../../runtime/doc/develop.txt) diff --git a/src/nvim/msgpack_rpc/server.c b/src/nvim/msgpack_rpc/server.c @@ -135,7 +135,7 @@ char *server_address_new(const char *name) return xstrdup(fmt); } -/// Check if this instance owns a pipe address. +/// Check if this instance owns a pipe address (loopback). bool server_owns_pipe_address(const char *address) { bool result = false; diff --git a/src/nvim/path.c b/src/nvim/path.c @@ -46,8 +46,7 @@ enum { /// Compare two file names. /// -/// @param s1 First file name. Environment variables in this name will be -/// expanded. +/// @param s1 First file name. Environment variables in this name will be expanded. /// @param s2 Second file name. /// @param checkname When both files don't exist, only compare their names. /// @param expandenv Whether to expand environment variables in file names. @@ -540,7 +539,6 @@ bool path_has_wildcard(const char *p) return false; } -// Unix style wildcard expansion code. static int pstrcmp(const void *a, const void *b) { return pathcmp(*(char **)a, *(char **)b, -1); @@ -1956,8 +1954,10 @@ bool same_directory(char *f1, char *f2) } // Compare path "p[]" to "q[]". -// If "maxlen" >= 0 compare "p[maxlen]" to "q[maxlen]" +// If `maxlen` >= 0 compare `p[maxlen]` to `q[maxlen]` // Return value like strcmp(p, q), but consider path separators. +// +// See also `path_full_compare`. int pathcmp(const char *p, const char *q, int maxlen) { int i, j; @@ -2314,12 +2314,12 @@ int append_path(char *path, const char *to_append, size_t max_len) return OK; } -/// Expand a given file to its absolute path. +/// Used by `vim_FullName` and `fix_fname` to expand a filename to its full path. /// -/// @param fname filename which should be expanded. -/// @param buf buffer to store the absolute path of "fname". -/// @param len length of "buf". -/// @param force also expand when "fname" is already absolute. +/// @param fname Filename to expand. +/// @param buf Where to store the absolute path of "fname". +/// @param len Length of `buf`. +/// @param force Also expand when `fname` is already absolute. /// /// @return FAIL for failure, OK for success. static int path_to_absolute(const char *fname, char *buf, size_t len, int force) diff --git a/test/functional/api/vim_spec.lua b/test/functional/api/vim_spec.lua @@ -4616,7 +4616,7 @@ describe('API', function() }, }, api.nvim_parse_cmd('4,6MyCommand! test it', {})) end) - it('works for commands separated by bar', function() + it('sets nextcmd for bar-separated commands', function() eq({ cmd = 'argadd', args = { 'a.txt' }, @@ -4655,6 +4655,12 @@ describe('API', function() }, }, api.nvim_parse_cmd('argadd a.txt | argadd b.txt', {})) end) + it('sets nextcmd after expr-arg commands #36029', function() + local result = api.nvim_parse_cmd('exe "ls"|edit foo', {}) + eq({ '"ls"' }, result.args) + eq('execute', result.cmd) + eq('edit foo', result.nextcmd) + end) it('parses :map commands with space in RHS', function() eq({ addr = 'none', @@ -4849,12 +4855,6 @@ describe('API', function() result = api.nvim_parse_cmd('copen 5', {}) eq(5, result.count) end) - it('parses nextcmd for commands #36029', function() - local result = api.nvim_parse_cmd('exe "ls"|edit foo', {}) - eq({ '"ls"' }, result.args) - eq('execute', result.cmd) - eq('edit foo', result.nextcmd) - end) end) describe('nvim_cmd', function()