mirror of
https://github.com/neovim/neovim.git
synced 2025-10-26 12:27:24 +00:00
Merge #36028 docs
This commit is contained in:
@@ -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:
|
||||
@@ -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
|
||||
|
||||
==============================================================================
|
||||
UI events
|
||||
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 *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|
|
||||
@@ -75,6 +124,269 @@ Common examples of non-fast code: regexp matching, wildcard expansion,
|
||||
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:
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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|.
|
||||
|
||||
@@ -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 (`<60>[2 q`) WHEN CHANGING MODES ~
|
||||
------------------------------------------------------------------------------
|
||||
NVIM SHOWS WEIRD SYMBOLS (`<60>[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.
|
||||
|
||||
@@ -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()|,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -250,6 +259,32 @@ Validations could include:
|
||||
This can be tricky to implement, and may be better suited for a |health|
|
||||
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*
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()|,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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' })
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user