Problem: No guidance for AI coding agents working in the Vim
repository.
Solution: Add AGENTS.md to the repository documenting build
and test commands, repository layout, commit format,
C and Vim9-script conventions, test conventions, help
file style, and release policy, so that AI agents can
produce patches that match project expectations.
closes: #20039
Signed-off-by: Christian Brabandt <cb@256bit.org>
15 KiB
AGENTS.md
Guidance for AI coding agents working in the Vim repository.
Project
Vim is a text editor written in C. The canonical repository is
https://github.com/vim/vim. The code is old and has grown organically over
the past 30+ years. Some files are vendored from upstream projects
(src/xdiff, src/libvterm); parts of the runtime are occasionally shared
with forks like Neovim.
Vim strives to be portable across several different operating systems and aims to be a stable, robust editor gradually developing new features while remaining backwards compatible as much as possible.
At the same time, Vim can be compiled with different feature sets, from the POSIX compatible minimal vi to a full-fledged GUI editor which includes additional scripting interfaces.
See runtime/doc/develop.txt for the high level design goals.
Build and test
# Full build on Unix/Linux (from src/):
make
# Run the full test suite:
make test
# Generate proto files
make proto
# Run a single test file:
cd src/testdir && make test_name.res
Output is in testdir/messages and testdir/test.log
Builds on Windows depend on the Environment, see src/INSTALLpc.txt
for Cygwin/MSYS and MSVC ways to build Vim
Before submitting any patch, at minimum:
- The build succeeds without new warnings.
- Relevant tests pass.
- The code matches the style of the file being edited.
Layout
src/- the C source. Subsystem names are usually obvious from filenames (buffer.c,window.c,search.c,vim9compile.c, etc.).src/proto/- function prototypes, one.profile per source file. Regenerated; do not hand-edit unless you know what you're doing.src/po- Translationssrc/xxd- for the xxd subprojectsrc/xdiff- for the xdiff library (imported from git)src/libvterm- for the libvterm librarysrc/testdir/- tests. Vim-script files namedtest_*.vim. Screendump expected output lives insrc/testdir/dumps/.runtime/doc/- user-facing documentation in Vim help format, when updating, also update the Last Change headerruntime/syntax/generator- Syntax script for Vim Script, automatically generated from Vims sourceruntime/- runtime files shipped with Vim, when updating, also update the Last Change header and a short description if this file has no maintainer If the file has a maintainer, changes should go via them (so make a merge request against the upstream repo instead)src/version.c- contains theincluded_patches[]list. Every patch touching anything belowsrc/(with the exception ofsrc/po) needs a new entry at the top, will be updated only when merging into the master tree.
Commit format
Vim uses a strict commit message format. The subject line is a one-sentence problem statement, not a description of the fix:
patch 9.2.NNNN: short description of the problem
Problem: Restatement of the problem as a full sentence, possibly
with a reporter attribution in parentheses.
Solution: Short description of the fix, ending with the author's
name in parentheses.
optional longer description of the problem and solution goes here in prose.
Do not use bullet points.
fixes: #NNNN
related: #NNNN
closes: #NNNN
Co-authored-by: Name
Signed-off-by: Author Name <email>
Rules:
- Subject line states the problem, not the solution. "fix typo" is wrong; "typo in foo() causes OOB read" is right.
- Problem line is a full sentence with a trailing period. It mirrors the subject.
- Solution line ends with
(Author Name)— parentheses, period after them. - Longer prose, if any, goes after the Problem/Solution header
fixes:references the issue the patch fixes.closes:references the PR that introduces the fix.related:references related issues, including issues that caused this one. All can appear. Colon, aligned, no trailing period.Signed-off-by:is required — DCO.Co-Authored-By:is allowed and is the accepted way to acknowledge AI assistance transparently. Human coauthors should usually also have their own Signed-off-by.
C code conventions
- Indentation is 4 spaces per level. Existing files use tabs with
ts=8 sts=4 sw=4 noet(set by the modeline in the file), so tabs of width 8 appear where two levels of indent collapse.sign.c,sound.c, and any new file must use spaces only and follow the style from the .editorconfig file. - Opening braces go on their own line (Allman style) — for function
definitions and for control-flow constructs (
if/else/for/while/do) alike. - Function definitions: return type on its own indented line, with the function name beginning on the next line.
- Initialize locals where a reader cannot trivially see the first
assignment (common for pointers and return-value accumulators).
Don't add
= 0initializers for values that are always assigned before use — they can hide real uninitialized-read bugs from the compiler. for (int i = 0; ...)loop declarations are fine in files that use them; older files may declare the counter at the top of the block.- Function-scope declarations at the top of a block is the historical style, but mid-block declarations are acceptable in files that have adopted them. Match the surrounding code.
- Custom types end in
_T(e.g.,buf_T,linenr_T,pos_T). Never use_t— it collides with POSIX typedefs. - C language is C95 plus specific C99 features:
//comments, mixed declarations and statements,__func__,bool/_Bool, variadic macros, compound literals,static inline, trailing enum commas. Do not reach for later C standards — Vim still must build with Compaq C on OpenVMS. See*assumptions-C-compiler*indevelop.txtfor the full list. bool/true/falseare acceptable. Vim is transitioning fromintwithTRUE/FALSEto C99bool. Do not "fix"boolback toint. Within a single patch, be consistent — don't mixtrueandTRUEin new code.- Do not mass-convert
TRUE/FALSEtotrue/falseacross files unless that is the patch's explicit purpose. Opportunistic conversions create noise in diffs. STRLEN_LITERAL("...")should be used when the length of a string literal is needed. AvoidSTRLEN()on literals.vim_snprintf_safelen()returns the written length; prefer it overvim_snprintf()when the length is then needed.- Prefer
dict_add_string_len()when the string length is already known, overdict_add_string()which callsSTRLEN(). - String/buffer parameters go
(char_u *buf, size_t buflen)— length alongside pointer, in bytes. Usesize_tfor byte counts,intonly where required by legacy APIs. - Guards before divisions. Check for divisor zero explicitly, even when a composite earlier guard would prevent it. Relying on transitive guards is fragile.
- When introducing new allocations, verify the cleanup paths handle all exit conditions (early return, error branches, etc).
Use Vim wrappers instead of libc where one exists:
| libc | Vim | Why |
|---|---|---|
free() |
vim_free() |
Tolerates NULL |
malloc() |
alloc() / lalloc() |
Checks for OOM |
strcpy() |
STRCPY() |
Cast for char_u * |
strchr() |
vim_strchr() |
Handles special characters |
strrchr() |
vim_strrchr() |
Handles special characters |
memcpy() |
mch_memmove() |
Handles overlapping copies |
bcopy() |
mch_memmove() |
Handles overlapping copies |
memset() |
vim_memset() |
Uniform across systems |
isspace() |
vim_isspace() |
Handles bytes > 127 |
iswhite() |
vim_iswhite() |
TRUE only for tab and space |
Further rules, not spelled out here, live in runtime/doc/develop.txt:
*style-names*— reserved name patterns (is*,to*,str*,mem*,wcs*,.*_t,__.*), forbidden identifiers (delete,this,new,time,index), and the 31-character function-name limit.*style-spaces*,*style-examples*— spacing and one-statement-per-line.*style-various*—FEAT_feature prefix, uppercase#define,#ifdef HAVE_Xrather than#if HAVE_X, no'\"'.*assumptions-makefiles*— POSIX.1-2001makeonly in the main Makefiles (no%rules,:=,.ONESHELL, GNU conditionals).- Vim uses
char_uinstead ofchartype - Vim uses the macros
STRLEN,STRCPY,STRCMP,STRCATthat work with thechar_utype. *style-clang-format*—sign.candsound.care formatted withclang-format; re-run it after editing those files.
Vim9 script conventions (in tests and runtime files)
- Write modern Vim style (new files can use Vim9 script, but compatibility with Neovim and other forks is a concern, so in doubt please ask!)
- Drop
l:prefix from local variables in Vim-script tests. - Don't add
CheckFeatureinside individual tests if it's already at the top of the file. - If a test file doesn't gate features at the top, add CheckFeature to individual tests that depend on specific build features.
Test conventions
- Tests are in
src/testdir/test_*.vim. - Reproducible tests beat "it doesn't crash" tests. If a patch fixes a rendering bug, add a screendump test. If it fixes incorrect output, assert the output.
- Add comprehensive tests for newly added features and include them in existing tests if possible
- Screendump tests use
CheckScreendump,RunVimInTerminal,VerifyScreenDump, and live dumps insrc/testdir/dumps/. v9.CheckScriptSuccess(lines)/v9.CheckScriptFailure(lines, error, lnum)are the standard way to test Vim9 script behavior at script-load time.- When fixing a bug reported as an issue, include a test that reproduces the original report, not just a minimal synthetic case.
- Tests for Syntax runtime are in
runtime/syntax/testdir - Tests for Indent runtime are in
runtime/indent/testdir
Common gotchas
- Distinguish what code enforces from what docs claim. If a patch changes documented behavior, say so in the Problem/Solution.
- Generated files (
src/auto/configure, generated Wayland protocol C, etc.) should only be regenerated when their source changes. Mixing unrelated regeneration into a functional patch creates noise.
Documentation
- User-facing option or feature changes require a
runtime/doc/*.txtupdate in the same patch. - When editing an existing help file, bump the
Last change:header at the top.
Help file style
See runtime/doc/helphelp.txt (*help-writing*) for the authoritative
reference. Key conventions:
- File header: first line is
*filename.txt*then a tab then a short description. That description appears underLOCAL ADDITIONSinhelp.txt. The version andLast change:date go on the second line, right aligned. - Modeline: every help file ends with a Vim modeline — typically
vim:tw=78:ts=8:noet:ft=help:norl:. - Layout:
'textwidth'78,'tabstop'8, indent and align with tab characters. Two spaces between sentences. Run:retab(not:retab!, and review the diff) after editing. - Tags are defined as
*tag-name*, usually right-aligned on the line where the thing they name is introduced. Tag names must be unique across all ofruntime/doc/; for plugin help, prefix with the plugin name. - Cross-references inside help text:
|tag-name|— hot-link to an existing tag.`:cmd`— Ex command, highlighted as a code block.'option'— option name, in single quotes.<Key>orCTRL-X— special keys.{placeholder}— user-supplied argument.
- Sections are separated by a line of
=starting in column 1. Column or subsection headings end with~to trigger heading highlighting. - Code blocks start with
>at the end of the introducing line and end with<as the first non-blank on a later line (any line starting in column 1 also implicitly closes the block). Use>vim(or another language name) to request syntax highlighting inside the block. - Notation —
Note,Todo,Errorand a few similar words are auto-highlighted; do not try to fake the highlighting by other means. - Language: gender-neutral language is preferred for new or updated text; existing wording does not need to be rewritten for this alone.
Release policy
Vim alternates between development cycles and stability periods — see
runtime/doc/develop.txt *design-policy*.
- During a stability period only clear bug fixes, security fixes, documentation updates, translations, and runtime file updates are accepted. No new features, no backwards-incompatible changes.
- Once released in a minor version, C-core features must stay backwards-compatible. Runtime files have a bit more flexibility so their maintainers can correct old behavior.
- Deprecated features stay reachable via config (do not hard-error), are documented as deprecated, can be disabled at compile time, and may be removed in a later cycle.
Security
Before reporting a suspected security issue or submitting a patch
that touches security-sensitive code, read SECURITY.md. Follow
the disclosure process described there.
Before submitting
- Commit message follows the format above.
- All modified code compiles without new warnings.
- Tests pass, and new functionality has regression tests.
- Documentation is updated for user-visible changes.
- Signed-off-by is present.
- Diff contains only changes relevant to the stated problem —
no stray whitespace fixes, no unrelated refactors, no unrelated
regeneration of
auto/configure. - For multi-patch series: each commit compiles and passes its own tests. A known-broken intermediate state that a later patch fixes is not acceptable — squash instead.
When in doubt
- Make the smallest possible change to achieve the goal. Do not rewrite entire files or functions when a targeted edit suffices.
- Read surrounding code and match its style rather than imposing an "improvement."
- Err toward smaller, more focused patches. A patch that does three things is three patches.
- If a patch fixes a symptom of a deeper bug, say so in the Problem and acknowledge the scope limitation in the Solution.
- Before claiming a bug exists, reproduce it. Before claiming code does X, read the code. Do not rely on training-data memory of file contents.
- Before running shell commands that modify files outside the working tree, install packages, push branches, or invoke network operations, confirm with the user.