neovim/neovim
Regex and search
Purpose
Two regex engines coexist in src/nvim/regexp.c — the legacy NFA-based "Vim regex" and the older backtracker. Both implement Vim's regex dialect (which is similar to but not identical to PCRE: \v, \<word\>, \zs, \ze, look-behind, etc.). The runtime picks one via the 'regexpengine' option ('re'); most of the editor's substring searching, completion, and :substitute go through this layer.
Directory layout
src/nvim/
├── regexp.c (~456k bytes / 16,309 lines) Both engines, fold-aware matching
├── regexp.h, regexp_defs.h
├── search.c (~118k bytes) :/ ?, * , #, n, N
├── search.h
├── fuzzy.c (~29k bytes) fuzzy matching for cmdline/wildmenu
├── fuzzy.h
└── api/private/.../ (API regex helpers)regexp.c is the largest single source file in the repository. Its size is owed to: two complete regex engines (NFA and backtracker), Vim-specific extensions (\<, \>, \zs, \ze, atom classes), multibyte handling, and the substitute-replacement parser.
Key abstractions
| Type / function | File | Description |
|---|---|---|
regprog_T |
regexp_defs.h |
A compiled regex. Tagged with the engine. |
regmatch_T |
regexp_defs.h |
A match result: prog + start positions + multi-line offsets. |
regmmatch_T |
regexp_defs.h |
Multi-line variant. |
vim_regcomp |
regexp.c |
Compile a pattern into a regprog_T. Picks an engine based on 're'. |
vim_regexec / vim_regexec_multi |
regexp.c |
Execute a compiled regex against a string or buffer range. |
searchit |
search.c |
The workhorse for / and ? — incremental search with options. |
do_sub |
ex_cmds.c |
:substitute. Calls into vim_regexec_multi. |
The two engines
Both engines compile the user's pattern to an internal program form and run it against input. They diverge in how they execute:
Backtracking engine (legacy)
regexp_bt.c is concatenated into regexp.c at compile time. It is the original Vim engine: a recursive descent that tries each alternative in order. It supports every Vim feature including back-references and look-behind, but suffers catastrophic backtracking on adversarial patterns.
NFA engine
regexp_nfa.c is also #included into regexp.c. It compiles to an NFA and runs Thompson-style — every state is advanced in lockstep, so worst-case complexity is O(n × m) for input length n and pattern length m. The NFA engine handles most but not all Vim features; for unsupported features the compile falls back to the backtracker.
The user picks via :set regexpengine=:
0(default) — try NFA, fall back to backtracker on compile failure.1— backtracker only.2— NFA only.
Search
src/nvim/search.c implements the user-facing search commands:
/patternand?pattern(forward/backward, normal-modenandN).*and#(search for word under cursor).gd,gD(local/global declaration jump).[i,]i, etc. (include-file search — uses the'include'option).searchpos(),search(),searchpair()— Vimscript and Lua surfaces.
The hot loop is searchit(). It walks the buffer line by line (ml_get), runs vim_regexec_multi against each line, and tracks position. Highlighting of matches ('hlsearch') is wired through the same compiled regex but as a match_T per window.
Smart case ('smartcase'), 'magic'/'nomagic', 'ignorecase', and the \c/\C flags interact in non-obvious ways. Read the pat_has_* helpers near the top of search.c if you're tracing a "case-sensitivity is wrong" bug.
Fuzzy matching
src/nvim/fuzzy.c implements the fuzzy match used by:
- The wildmenu (
'wildoptions=fuzzy'). :luaautocomplete.vim.fn.matchfuzzyandmatchfuzzypos.- Some Lua plugins via
vim.fn.matchfuzzy().
It is a small Smith-Waterman variant tuned for short patterns. The scoring rewards initials, consecutive matches, and word boundaries. The implementation is straightforward; the match function is called once per candidate and produces a (score, positions[]) pair.
Substitute
:substitute/old/new/flags is implemented in src/nvim/ex_cmds.c#ex_substitute. The flow:
- Parse the delimiter, the pattern, the replacement, and the flags.
- Compile the pattern via
vim_regcomp. - Walk lines in the range; on each line, repeatedly call
vim_regexec_multi. - For each match, parse the replacement (
\0,\1,&,\=expr,\,r/\,l, etc.). - Build the new line via
regtilde()andvim_regsub(). - Apply via
ml_replace(withu_savefirst). - With
cflag: prompt; withgflag: continue past the first match per line.
The \=expr form runs an arbitrary Vimscript expression for the replacement, which is what makes :s/foo/\=submatch(0).get_count()/g work. That path goes through eval in src/nvim/eval/.
Integration points
- Vimscript —
match(),search(),searchpos(),searchpair(),=~,=~#,=~?operators insrc/nvim/eval.c. - Lua —
vim.regex(pattern)returns a wrapper. The compile and exec are the same C code; only the binding differs. - Treesitter queries — not this engine. Tree-sitter has its own pattern language, with C bindings in
src/nvim/lua/treesitter.c. - Syntax — old-style
:syntax matchand:syntax regionuse this engine for their patterns. - Subsitute on a yank —
:sworks ongetreg()content becausevim_regexecoperates on strings, not just buffers.
Entry points for modification
- Bug in match behavior. Step into
vim_regexec_multi, then dispatch to eitherregexp_bt.c(backtracker) orregexp_nfa.c(NFA) inside the same translation unit. Both have extensive comments at the top. - New regex feature. Almost always means adding a parser case in both engines — that, plus updating the test corpus in
test/old/testdir/test_regexp_*andtest/functional/. - New search command. Most live in
search.c. The pattern is parse-flags + callsearchitwith the right options. - Replacement syntax.
regtildeandvim_regsubinregexp.c.
Key source files
| File | Purpose |
|---|---|
src/nvim/regexp.c |
Both engines, compile + exec, replacement substitution |
src/nvim/regexp_defs.h |
regprog_T, regmatch_T, related types |
src/nvim/search.c |
/, ?, n, N, *, #, searchpair, searchpos |
src/nvim/fuzzy.c |
Fuzzy matching for cmdline and wildmenu |
src/nvim/ex_cmds.c#ex_substitute |
:substitute |
runtime/doc/pattern.txt |
User-facing pattern reference |
Built by Factory AutoWiki from public repository content. It is a generated preview for codebase exploration, not source-maintained documentation.