Nvim :help pages, generated
from source
using the tree-sitter-vimdoc parser.
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.
*.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.generated.h - exported functions’ declarations.
*.c.generated.h - static functions’ declarations.
kvec.h for most lists. When you absolutely need a linked list, use
lib/queue_defs.h which defines an "intrusive" linked list.
src/nvim/memline.c.
The central idea is found in ml_find_line.
event/defs.h#Event struct, which is just a bag of data passed along the
internal event-loop.)
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.
redraw notification. They also can be listened to
in-process via vim.ui_attach().
src/nvim/ui.*: calls handler functions of registered UI structs (independent from msgpack-rpc)
2. src/nvim/api/ui.*: forwards messages over msgpack-rpc to remote UIs.
src/nvim/api/ui_events.in.h , this file is not
compiled directly, rather it parsed by
src/nvim/generators/gen_api_ui_events.lua which autogenerates wrapper
functions used by the source files above. It also generates metadata
accessible as api_info().ui_events.
os_breakcheck(), which may
"yield" the current execution and start a new execution of code not expecting
this:
os_breakcheck() cannot
be "fast". For example, commit 3940c435e405 fixed such a bug with
nvim__get_runtime by explicitly disallowing os_breakcheck() via the
EW_NOBREAK flag.
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
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
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.
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.
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;
:
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
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
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
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:
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.
state_enter loop:
command_line_{enter,check,execute}()(ex_getln.c)
insert_{enter,check,execute}()(edit.c)
terminal_{enter,execute}()(terminal.c)
State. The values it can have are MODE_NORMAL,
MODE_INSERT, MODE_CMDLINE, and a few others.
curwin. The current buffer is curbuf. These point
to structures with the cursor position in the window, option values, the file
name, etc.
globals.h.
vgetc() function is used for this. It also handles mapping.
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).
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.
:, 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.
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.
: commands are listed in [ex_cmds.lua](ex_cmds.lua).
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.
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.
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.