Nvim:help pages,generated fromsource using thetree-sitter-vimdoc parser.
BUILD.md.2. Clone the source repo and "cd" into it:git clone https://github.com/neovim/neovimcd neovim# (Optional) Build and run Nvim:makeVIMRUNTIME=./runtime ./build/bin/nvim --luamod-dev3. Run a single test. We will start with "example_spec.lua", which is a real test that shows how tests are written:
make functionaltest TEST_FILE=test/functional/example_spec.lua4. Notice the
before_each block in the test file. Because it callsclear(), eachit() test will start a new Nvim instance.5. Tests will do stuff in the Nvim instance and make assertions usingeq(). Tests that want to check the UI can also usescreen:expect().6. Now make a code change in Nvim itself, then you can see the effects. The example test doesfeed('iline1…'), so let's make a change to the insert-mode code, which lives insrc/nvim/edit.c. In theinsert_handle_key function, just after thenormalchar label, add this code:s->c = 'x';7. Then run the "example_spec.lua" test again, and it should fail with something like this:
test/functional/example_spec.lua:31: Row 1 did not match.Expected: |*line1 | |*line^2 | |{0:~ }| |{0:~ }| | |Actual: |*xine1 | |*xine^2 | |{0:~ }| |{0:~ }| | |You now understand how to modify the codebase, write tests, and run tests. Seedev-arch for details about the internal architecture.$NVIM_LOG_FILE.rm -rf build/make CMAKE_EXTRA_FLAGS="-DLOG_DEBUG"Use
LOG_CALLSTACK() (Linux only) to log the current stacktrace. To log to analternate file (e.g. stderr) useLOG_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 tofilter 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
LUA_GEN_PRG toa LuaJIT binary built withLUAJIT_SECURITY_PRN=0. See commitcb757f2663e6950e655c6306d713338dfa66b18d.nvim -V3log
--headlessflag which does not launch the TUI and will allow you to set breakpoints in codenot related to TUI rendering like so:lldb -- ./build/bin/nvim --headless --listen ~/.cache/nvim/debug-server.pipeWhile in lldb, enter
run. You can then attach to the headless process in anew terminal window to interact with the editor like so:./build/bin/nvim --remote-ui --server ~/.cache/nvim/debug-server.pipeConversely for debugging TUI rendering, you can start a headless process anddebug the remote-ui process multiple times without losing editor state.
:set writedelay=50 rdb=compositorNote: Nvim uses an internal screenbuffer to only send minimal updates even if a largeregion is repainted internally. To also highlight excess internal redraws, use
:set writedelay=50 rdb=compositor,nodelta
script command is the "state of the art". Thelibvtermvterm-dump utility formats the result for human-readability.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 > barThen you can comparebar with another session, to debug TUI behavior.man terminfoperf +flamegraph.NVIM_PROBE ([#12036](https://github.com/neovim/neovim/pull/12036)).#!/usr/bin/env bpftraceBEGIN { @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.bt1527 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 | |ulimit -c unlimitedcoredumpctl -1 gdbcoredumpctl is an optional tool, so you may need to install it:sudo apt install systemd-coredumpbacktrace.txt filewhen reporting a bug related to a crash:2>&1 coredumpctl -1 gdb | tee -a backtrace.txt(gdb) thread apply all bt fullcoredumpctl, you may find acore dump file appearingin the current directory or in other locations. On Linux systems whereapport is installed (such as Ubuntu), the directory where core dump filesare saved can be/var/lib/apport/coredump or elsewhere, depending on thesystem configuration (see/proc/sys/kernel/core_pattern). See also:https://stackoverflow.com/a/18368068./core dump file:gdb build/bin/nvim ./core 2>&1 | tee backtrace.txt(gdb) thread apply all bt fullnvim crashes, you can see the backtrace inConsole.app (under "CrashReports" or "User Diagnostic Reports" for older macOS versions).open -a Console/cores/ directory exists and is writable:sudo mkdir /coressudo chown root:admin /coressudo chmod 1775 /coresunlimited:ulimit -c unlimited~/.bashrcor similar).lldb:lldb -c /cores/core.12345/etc/launchd.conf).TEST_TAG to run tests matching busted tags (of the form#foo e.g.it("test #foo ...", ...)):GDB=1 TEST_TAG=foo make functionaltestgdb build/bin/nvim(gdb) target remote localhost:7777-- Seenvim_argv inhttps://github.com/neovim/neovim/blob/master/test/functional/testnvim.lua.lldb .deps/usr/bin/luajit -- .deps/usr/bin/busted --lpath="./build/?.lua" test/unit/
nvim process with a pid of 1234 (Tip: the pid of arunning Nvim instance can be obtained by callinggetpid()), for instance:gdb -tui -p 1234 build/bin/nvimgdb interactive prompt will appear. At any time you can:break foo to set a breakpoint on thefoo() functionn to step over the next statement<Enter> to repeat the last commands to step into the next statementc to continuefinish to step out of the current functionp zub to print the value ofzubbt to see a backtrace (callstack) from the current locationCTRL-x CTRL-a ortui enable to show a TUI view of the source file in the current debugging context. This can be extremely useful as it avoids the need for a gdb "frontend".<up> and<down> to scroll the source file viewset record full insn-number-max unlimitedcontinue for a bit (at least untilmain() is executedrecordrevert-next,reverse-step, etc. to rewind the debuggergdb clients to the same runningnvimprocess, or you may want to connect to a remotenvim process with a localgdb. Usinggdbserver, you can attach to a single process and control itfrom multiplegdb clients.gdbserver attached tonvim like this:gdbserver :6666 build/bin/nvim 2> gdbserver.loggdbserver is now listening on port 6666. You then need to attach to thisdebugging session in another terminal:gdb build/bin/nvimgdb, you need to attach to the remote session:(gdb) target remote localhost:6666
signal (SIGTTOU, SIG_IGN);if (!tcsetpgrp(data->input.in_fd, getpid())) { perror("tcsetpgrp failed");}tui.c:terminfo_start.gdbserver method mentioned above.This examplelocal.mk will create the debugging session when you typemake debug..PHONY: dbg-start dbg-attach debug buildbuild: @$(MAKE) nvimdbg-start: build @tmux new-window -n 'dbg-neovim' 'gdbserver :6666 ./build/bin/nvim -D'dbg-attach: @tmux new-window -n 'dbg-cgdb' 'cgdb -x gdb_start.sh ./build/bin/nvim'debug: dbg-start dbg-attachgdb_start.sh includesgdb commands to be called when the debuggerstarts. It needs to attach to the server started by thedbg-start rule. Forexample:(gdb) target remote localhost:6666(gdb) br main
llvm-symbolizer must be in$PATH:clang --versionBuild 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.