neovim/src/nvim
zeertzjq 950a88d4c2 vim-patch:8.2.4248: no proper test for moving the window separator
Problem:    No proper test for moving the window separator.
Solution:   Add a test.  Add comment in code. (closes vim/vim#9656)
a0c4e2f2d7

Remove the assertion as it is now possible for `fr` to be `NULL`.

The test fails without clearing messages. Not sure if this is a bug.
2022-01-29 20:37:48 +08:00
..
api Merge pull request #17183 from notomo/expose-extmark-right-gravity 2022-01-28 12:38:56 +01:00
eval vim-patch:8.2.4052: not easy to resize a window from a plugin (#17028) 2022-01-29 20:37:07 +08:00
event chore: fix typos (#16506) 2021-12-28 18:15:16 +01:00
generators feat(api): implement nvim_{add,del}_user_command 2021-12-28 14:08:44 -07:00
lib chore: fix typos (#16361) 2021-11-27 11:10:48 -05:00
lua feat(ts): expose minimum language version to lua (#17186) 2022-01-27 12:46:56 +01:00
msgpack_rpc refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
os fix(input): never escape CSI bytes 2022-01-21 18:08:56 +08:00
po vim-patch:fd31be29b822 (#17114) 2022-01-17 11:58:36 +01:00
testdir vim-patch:8.2.4248: no proper test for moving the window separator 2022-01-29 20:37:48 +08:00
tui chore: fix typos (#16506) 2021-12-28 18:15:16 +01:00
viml/parser chore: fix typos (#16816) 2022-01-04 11:07:40 -07:00
arabic.c refactor: format with uncrustify #15722 2021-09-19 13:07:42 -07:00
arabic.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
ascii.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
assert.h refactor: upgrade uncrustify config version to 0.74.0 2021-11-23 10:28:49 +01:00
aucmd.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
aucmd.h
auevents.lua docs(autocmd): update docs to match implementation 2021-12-18 08:54:07 -07:00
autocmd.c fix: update last cursor on first CursorMoved (#16698) 2021-12-18 19:18:47 -08:00
autocmd.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
buffer_defs.h Merge pull request #17095 from zeertzjq/vim-8.2.3227 2022-01-27 08:34:57 +01:00
buffer_updates.c refactor: format with uncrustify #15722 2021-09-19 13:07:42 -07:00
buffer_updates.h
buffer.c Merge pull request #17150 from zeertzjq/vim-8.2.4166 2022-01-27 08:48:31 +01:00
buffer.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
change.c vim-patch:8.2.3227: 'virtualedit' can only be set globally 2022-01-15 19:21:17 +08:00
change.h refactor(misc1): move out misc functions which obviously belong elsewhere 2021-12-09 23:17:29 +01:00
channel.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
channel.h feat(--headless): add on_print callback to stdioopen 2022-01-24 13:59:16 +01:00
charset.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
charset.h lint (#16526) 2021-12-05 23:33:22 -05:00
CMakeLists.txt feat: filetype.lua (#16600) 2022-01-04 07:28:29 -07:00
context.c vim-patch:8.1.0743: giving error messages is not flexible 2021-11-01 06:41:28 -04:00
context.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
cursor_shape.c refactor: saner options for uncrustify (#16204) 2021-11-19 14:21:53 -05:00
cursor_shape.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
cursor.c vim-patch:8.2.3227: 'virtualedit' can only be set globally 2022-01-15 19:21:17 +08:00
cursor.h
debugger.c refactor: saner options for uncrustify (#16204) 2021-11-19 14:21:53 -05:00
debugger.h vim-patch:8.1.1195: Vim script debugger functionality needs cleanup 2021-09-13 17:19:15 +01:00
decoration.c refactor(extmarks): use a more efficient representation 2022-01-15 22:08:12 +01:00
decoration.h feat(api): expose extmark more details 2022-01-20 13:25:46 +09:00
diff.c vim-patch:8.1.2302: :lockmarks does not work for '[ and '] 2022-01-27 18:47:52 +08:00
diff.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
digraph.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
digraph.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
edit.c fix(completion): update submode message when selecting from API (#17022) 2022-01-28 07:55:10 +08:00
edit.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
eval.c fix(eval): v:lua support for - in module names 2022-01-28 18:20:26 +01:00
eval.h refactor(misc1): move out autocmd related functions 2021-12-09 21:10:58 +01:00
eval.lua vim-patch:8.2.4052: not easy to resize a window from a plugin (#17028) 2022-01-29 20:37:07 +08:00
ex_cmds2.c vim-patch:8.2.4133: output of ":scriptnames" goes into the message history 2022-01-27 15:55:06 +08:00
ex_cmds2.h feat(:source, nvim_exec): defer script item creation until s:var access 2021-10-14 12:50:04 +01:00
ex_cmds_defs.h vim-patch:8.2.3584: "verbose set efm" reports location of the :compiler command 2022-01-21 18:18:18 +08:00
ex_cmds.c Merge pull request #16820 from seandewar/vim-8.1.2302 2022-01-27 23:23:26 +00:00
ex_cmds.h vim-patch:8.2.3547: opening the quickfix window triggers BufWinEnter twice (#16108) 2021-10-23 21:55:04 -04:00
ex_cmds.lua vim-patch:8.2.0128: cannot list options one per line 2022-01-20 14:34:24 +08:00
ex_docmd.c Merge pull request #16829 from zeertzjq/vim-8.2.2887 2022-01-29 02:42:14 +08:00
ex_docmd.h feat(api): implement nvim_{add,del}_user_command 2021-12-28 14:08:44 -07:00
ex_eval.c lint (#16526) 2021-12-05 23:33:22 -05:00
ex_eval.h lint (#16526) 2021-12-05 23:33:22 -05:00
ex_getln.c vim-patch:8.2.4214: illegal memory access with large 'tabstop' in Ex mode 2022-01-26 21:34:26 +01:00
ex_getln.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
ex_session.c vim-patch:8.2.4007: session does not restore help buffer properly 2022-01-07 13:39:52 +08:00
ex_session.h
extmark_defs.h refactor(extmarks): use a more efficient representation 2022-01-15 22:08:12 +01:00
extmark.c feat(api): expose extmark right_gravity and end_right_gravity 2022-01-24 09:52:13 +09:00
extmark.h feat(api): expose extmark right_gravity and end_right_gravity 2022-01-24 09:52:13 +09:00
file_search.c vim-patch:8.2.3613: :find test fails 2022-01-18 10:05:31 +08:00
file_search.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
fileio.c vim-patch:8.1.2302: :lockmarks does not work for '[ and '] 2022-01-27 18:47:52 +08:00
fileio.h vim-patch:8.2.3547: opening the quickfix window triggers BufWinEnter twice (#16108) 2021-10-23 21:55:04 -04:00
fold.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
fold.h lint (#16526) 2021-12-05 23:33:22 -05:00
func_attr.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
garray.c refactor: reduce number of unique char casts 2021-10-13 18:26:18 +02:00
garray.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
getchar.c fix(input): remove reinterpreted ALT/META chords from recorded macro 2022-01-29 06:05:14 +08:00
getchar.h feat(api): add support for lua function & description in keymap 2022-01-01 00:26:58 +06:00
gettext.h vim-patch:8.1.0306: plural messages are not translated properly 2021-11-01 06:40:00 -04:00
globals.h Merge pull request #17166 from Shougo/vim-8.2.4160 2022-01-27 08:52:08 +01:00
grid_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
hardcopy.c lint (#16526) 2021-12-05 23:33:22 -05:00
hardcopy.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
hashtab.c vim-patch:8.1.0743: giving error messages is not flexible 2021-11-01 06:41:28 -04:00
hashtab.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
highlight_defs.h vim-patch:8.2.3664: cannot adjust sign highlighting for 'cursorline' 2021-12-08 21:47:58 -05:00
highlight.c vim-patch:8.1.0743: giving error messages is not flexible 2021-11-01 06:41:28 -04:00
highlight.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
iconv.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
if_cscope_defs.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
if_cscope.c lint (#16526) 2021-12-05 23:33:22 -05:00
if_cscope.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
indent_c.c chore: fix compiler warning for multi-line comment (#16812) 2021-12-28 15:55:28 +01:00
indent_c.h
indent.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
indent.h
input.c refactor: remove CSI unescaping and clean up related names and comments 2022-01-21 18:08:56 +08:00
input.h refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
keymap.c refactor: remove CSI unescaping and clean up related names and comments 2022-01-21 18:08:56 +08:00
keymap.h refactor: remove CSI unescaping and clean up related names and comments 2022-01-21 18:08:56 +08:00
log.c refactor: reformat with uncrustify #15736 2021-09-20 09:35:41 -07:00
log.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
macros.h refactor: reduce number of explicit char casts (#16077) 2021-11-16 20:27:59 +01:00
main.c refactor: source ftplugin.vim separately from filetype.vim (#17129) 2022-01-18 12:46:41 -07:00
main.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
map_defs.h refactor: remove space after star 2021-10-19 15:20:33 +02:00
map.c refactor(extmarks): use a more efficient representation 2022-01-15 22:08:12 +01:00
map.h refactor(extmarks): use a more efficient representation 2022-01-15 22:08:12 +01:00
mark_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
mark.c refactor: saner options for uncrustify (#16204) 2021-11-19 14:21:53 -05:00
mark.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
marktree.c feat(api): expose extmark right_gravity and end_right_gravity 2022-01-24 09:52:13 +09:00
marktree.h refactor: include missing assert header 2022-01-26 20:44:45 +01:00
math.c refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
math.h
mbyte.c Merge pull request #16830 from kuuote/vim-8.2.3571 2022-01-28 12:21:15 +08:00
mbyte.h refactor(multibyte): eliminate mb_char2len alias for utf_char2len 2021-11-14 12:49:12 +01:00
memfile_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
memfile.c chore(fixup): bfredl size_t 2022-01-01 02:55:34 +01:00
memfile.h
memline_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
memline.c refactor(PVS/V1048): ignore "assigned the same value" warning 2022-01-08 21:11:43 +01:00
memline.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
memory.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
memory.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
menu.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
menu.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
message.c Merge pull request #15910 from glacambre/silent_stdioopen 2022-01-24 22:33:55 +01:00
message.h vim-patch:8.1.0779: argument for message functions is inconsistent 2021-11-01 06:41:29 -04:00
mouse.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
mouse.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
move.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
move.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
normal.c vim-patch:8.2.4237: record buffer wrong if character in Select mode was not typed 2022-01-28 20:53:56 +08:00
normal.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
ops.c vim-patch:8.1.2302: :lockmarks does not work for '[ and '] 2022-01-27 18:47:52 +08:00
ops.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
option_defs.h vim-patch:8.2.4094: 'virtualedit' is window-local but using buffer-local enum 2022-01-15 19:21:17 +08:00
option.c Merge pull request #16954 from f380cedric/vim-8.2.3402 2022-01-27 23:05:43 +00:00
option.h vim-patch:8.2.0128: cannot list options one per line 2022-01-20 14:34:24 +08:00
options.lua vim-patch:8.2.4094: 'virtualedit' is window-local but using buffer-local enum 2022-01-15 19:21:17 +08:00
os_unix.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
os_unix.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
path.c Merge pull request #17174 from zeertzjq/vim-8.2.3153 2022-01-27 08:32:02 +01:00
path.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
plines.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
plines.h refactor(plines): move out plines related code from misc1.c 2021-08-10 22:59:29 +02:00
popupmnu.c refactor(pum_redraw): rename col -> grid_col 2021-12-24 08:06:27 +08:00
popupmnu.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
pos.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
profile.c refactor: format with uncrustify #15755 2021-09-24 05:03:15 -07:00
profile.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
quickfix.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
quickfix.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
rbuffer.c refactor: format with uncrustify #15755 2021-09-24 05:03:15 -07:00
rbuffer.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
README.md docs #15447 2021-08-21 18:26:33 -07:00
regexp_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
regexp_nfa.c vim-patch:8.2.3612: using freed memory with regexp using a mark (#16973) 2022-01-27 14:59:30 +01:00
regexp.c vim-patch:8.2.3612: using freed memory with regexp using a mark (#16973) 2022-01-27 14:59:30 +01:00
regexp.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
runtime.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
runtime.h fix(runtime): don't use regexes inside lua require'mod' 2021-10-17 16:21:42 +02:00
screen.c vim-patch:8.2.3095: with 'virtualedit' set to "block" block selection is wrong 2022-01-27 18:55:21 +08:00
screen.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
search.c Merge pull request #16607 from bfredl/no2misc1 2021-12-10 23:38:00 +01:00
search.h refactor: saner options for uncrustify #16196 2021-10-31 17:03:08 -07:00
sha256.c refactor: format with uncrustify #15778 2021-09-25 17:16:04 -07:00
sha256.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
shada.c refactor: saner options for uncrustify (#16204) 2021-11-19 14:21:53 -05:00
shada.h
sign_defs.h vim-patch:8.2.3664: cannot adjust sign highlighting for 'cursorline' 2021-12-08 21:47:58 -05:00
sign.c vim-patch:8.2.3874: cannot highlight the number column for a sign 2021-12-22 15:03:32 -05:00
sign.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
spell_defs.h refactor: uncrustify #16090 2021-10-29 17:23:20 -07:00
spell.c vim-patch:8.2.3582: reading uninitialized memory when giving spell suggestions 2022-01-06 18:47:31 +01:00
spell.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
spellfile.c vim-patch:8.2.3914 (#16808) 2021-12-28 11:28:48 +01:00
spellfile.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
state.c vim-patch:8.2.3227: 'virtualedit' can only be set globally 2022-01-15 19:21:17 +08:00
state.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
strings.c vim-patch:8.2.3663: using %S in printf() does not work correctly 2022-01-16 17:37:06 +08:00
strings.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
syntax_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
syntax.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
syntax.h refactor: reduce number of unique char casts (#15995) 2021-10-12 17:52:54 +02:00
tag.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
tag.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
terminal.c fix(input): put modifiers back into typeahead buffer when needed 2022-01-23 05:58:32 +08:00
terminal.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
types.h refactor(extmarks): use a more efficient representation 2022-01-15 22:08:12 +01:00
ugrid.c refactor: format with uncrustify #15872 2021-10-02 14:41:50 -07:00
ugrid.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
ui_bridge.c refactor: format with uncrustify #15872 2021-10-02 14:41:50 -07:00
ui_bridge.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
ui_compositor.c refactor: reduce number of unique char casts (#15995) 2021-10-12 17:52:54 +02:00
ui_compositor.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
ui.c refactor(misc1): move out high-level input functions to a new file: input.c 2021-12-10 18:15:33 +01:00
ui.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
undo_defs.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
undo.c vim-patch:8.2.4217: illegal memory access when undo makes Visual area invalid 2022-01-26 22:01:19 +01:00
undo.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00
version.c version.c: update [skip ci] 2021-11-19 03:07:56 +00:00
version.h refactor: remove space after star 2021-10-19 15:20:33 +02:00
vim.h feat(api): implement nvim_{add,del}_user_command 2021-12-28 14:08:44 -07:00
window.c vim-patch:8.2.4248: no proper test for moving the window separator 2022-01-29 20:37:48 +08:00
window.h refactor: format all C files under nvim/ #15977 2021-10-12 07:56:52 -07:00

Nvim core

Module-specific details are documented at the top of each module (terminal.c, screen.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.

Logs

Low-level log messages sink to $NVIM_LOG_FILE.

UI events are logged at DEBUG level (DEBUG_LOG_LEVEL).

rm -rf build/
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0"

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):

rm -rf build/
make CMAKE_EXTRA_FLAGS="-DMIN_LOG_LEVEL=0 -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 ~/.cache/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="-DCLANG_ASAN_UBSAN=ON"
CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_MSAN=ON"
CC=clang make CMAKE_EXTRA_FLAGS="-DCLANG_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).
export ASAN_OPTIONS="detect_leaks=0:log_path=$HOME/logs/asan"
# Show backtraces in the logs.
export UBSAN_OPTIONS=print_stacktrace=1
export MSAN_OPTIONS="log_path=${HOME}/logs/tsan"
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

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).

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 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' option to see where and when the UI is painted.

:set writedelay=1

Terminal reference

Nvim lifecycle

Following describes how Nvim processes input.

Consider a typical Vim-like editing session:

  1. Vim displays the welcome screen
  2. User types: :
  3. Vim enters command-line mode
  4. User types: edit README.txt<CR>
  5. Vim opens the file and returns to normal mode
  6. User types: G
  7. Vim navigates to the end of the file
  8. User types: 5
  9. Vim enters count-pending mode
  10. User types: d
  11. Vim enters operator-pending mode
  12. User types: w
  13. Vim deletes 5 words
  14. User types: g
  15. Vim enters the "g command mode"
  16. User types: g
  17. Vim goes to the beginning of the file
  18. User types: i
  19. Vim enters insert mode
  20. User types: word<ESC>
  21. 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:

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:

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)

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:

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:

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 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;