neovim/CONTRIBUTING.md

14 KiB

Contributing to Neovim

Getting started

If you want to help but don't know where to start, here are some low-risk/isolated tasks:

  • Try a complexity:low issue.
  • Fix bugs found by Coverity.
  • Merge a Vim patch (requires strong familiarity with Vim)
    • NOTE: read the above link before sending improvements to "runtime files" (anything in runtime/).
      • Vimscript and documentation files are (mostly) maintained by Vim, not Nvim.
      • Lua files are maintained by Nvim.

Reporting problems

  • Check the FAQ.
  • Search existing issues (including closed!)
  • Update Neovim to the latest version to see if your problem persists.
  • Try to reproduce with nvim --clean ("factory defaults").
  • If a specific configuration or plugin is necessary to recreate the problem, use the minimal template in contrib/minimal.lua with nvim --clean -u contrib/minimal.lua after making the necessary changes.
  • Bisect your config: disable plugins incrementally, to narrow down the cause of the issue.
  • Bisect Neovim's source code to find the cause of a regression, if you can. This is extremely helpful.
  • When reporting a crash, include a stacktrace.
  • Use ASAN/UBSAN to get detailed errors for segfaults and undefined behavior.
  • Check the logs. :edit $NVIM_LOG_FILE
  • Include cmake --system-information for build-related issues.

Developer guidelines

  • Read :help dev and :help dev-doc if you are working on Nvim core.
  • Read :help dev-ui if you are developing a UI.
  • Read :help dev-api-client if you are developing an API client.
  • Install ninja for faster builds of Nvim.
    sudo apt-get install ninja-build
    make distclean
    make  # Nvim build system uses ninja automatically, if available.
    
  • Install ccache for faster rebuilds of Nvim. Nvim will use it automatically if it's found. To disable caching use:
    CCACHE_DISABLE=true make
    

Pull requests (PRs)

  • To avoid duplicate work, create a draft pull request.
  • Your PR must include test coverage.
  • Avoid cosmetic changes to unrelated files in the same commit.
  • Use a feature branch instead of the master branch.
  • Use a rebase workflow for all PRs.
    • After addressing review comments, it's fine to force-push.

Merging to master

For maintainers: when a PR is ready to merge to master,

  • prefer Squash Merge for "single-commit PRs" (when the PR has only one meaningful commit).
  • prefer Merge for "multi-commit PRs" (when the PR has multiple meaningful commits).

Stages: Draft and Ready for review

Pull requests have two stages: Draft and Ready for review.

  1. Create a Draft PR while you are not requesting feedback as you are still working on the PR.
    • You can skip this if your PR is ready for review.
  2. Change your PR to ready when the PR is ready for review.
    • You can convert back to Draft at any time.

Do not add labels like [RFC] or [WIP] in the title to indicate the state of your PR: this just adds noise. Non-Draft PRs are assumed to be open for comments; if you want feedback from specific people, @-mention them in a comment.

Commit messages

Follow the conventional commits guidelines to make reviews easier and to make the VCS/git logs more valuable. The general structure of a commit message is:

<type>([optional scope]): <description>

[optional body]

[optional footer(s)]
  • Prefix the commit subject with one of these types:
    • build, ci, docs, feat, fix, perf, refactor, revert, test, vim-patch
    • You can ignore this for "fixup" commits or any commits you expect to be squashed.
  • Append optional scope to type such as (lsp), (treesitter), (float), …
  • Description shouldn't start with a capital letter or end in a period.
  • Use the imperative voice: "Fix bug" rather than "Fixed bug" or "Fixes bug."
  • Try to keep the first line under 72 characters.
  • A blank line must follow the subject.
  • Breaking API changes must be indicated by
    1. "!" after the type/scope, and
    2. a "BREAKING CHANGE" footer describing the change. Example:
      refactor(provider)!: drop support for Python 2
      
      BREAKING CHANGE: refactor to use Python 3 features since Python 2 is no longer supported.
      

News

High level release notes are maintained in news.txt. A PR is not required to add a news item but is generally recommended.

Automated builds (CI)

Each pull request must pass the automated builds on Cirrus CI and GitHub Actions.

  • CI builds are compiled with -Werror, so compiler warnings will fail the build.
  • If any tests fail, the build will fail. See test/README.md#running-tests to run tests locally.
  • CI runs ASan and other analyzers.
    • To run valgrind locally: VALGRIND=1 make test
    • To run ASan/UBSan locally: CC=clang make CMAKE_FLAGS="-DENABLE_ASAN_UBSAN=ON". Note that MSVC requires Release or RelWithDebInfo build type to work properly.
  • The lint build checks that the code is formatted correctly and passes various linter checks.
  • CI for FreeBSD runs on Cirrus CI.
  • To see CI results faster in your PR, you can temporarily set TEST_FILE in test.yml.

Coverity

Coverity runs against the master build. To view the defects you must request access (Coverity does not have a "public" view), then you will be approved as soon as a maintainer sees the email.

  • Use this format for commit messages (where {id} is the CID (Coverity ID); (example)):
    fix(coverity/{id}): {description}
    
  • Search the Neovim commit history to find examples:
    git log --oneline --no-merges --grep coverity
    

Sanitizers (ASAN and UBSAN)

ASAN/UBSAN can be used to detect memory errors and other common forms of undefined behavior at runtime in debug builds.

  • To build Neovim with sanitizers enabled, use
    rm -rf build && CMAKE_EXTRA_FLAGS="-DCMAKE_C_COMPILER=clang -DENABLE_ASAN_UBSAN=1" make
    
  • When running Neovim, use
    ASAN_OPTIONS=log_path=/tmp/nvim_asan nvim args...
    
  • If Neovim exits unexpectedly, check /tmp/nvim_asan.{PID} (or your preferred log_path) for log files with error messages.

Coding

Lint

You can run the linter locally by:

make lint

Style

  • You can format files by using:
    make format  # or formatc, formatlua
    
    This will format changed Lua and C files with all appropriate flags set.
  • Style rules are (mostly) defined by src/uncrustify.cfg which tries to match the style-guide. To use the Nvim gq command with uncrustify:
    if !empty(findfile('src/uncrustify.cfg', ';'))
      setlocal formatprg=uncrustify\ -q\ -l\ C\ -c\ src/uncrustify.cfg\ --no-backup
    endif
    
  • There is also .clang-format which has drifted from the style-guide, but is available for reference. To use the Nvim gq command with clang-format:
    if !empty(findfile('.clang-format', ';'))
      setlocal formatprg=clang-format\ -style=file
    endif
    

Navigate

Includes

For managing includes in C files, use include-what-you-use.

  • Install include-what-you-use
  • To see which includes needs fixing use the cmake preset iwyu:
    cmake --preset iwyu
    cmake --build build
    
  • There's also a make target that automatically fixes the suggestions from IWYU:
    make iwyu
    

See #549 for more details.

Lua runtime files

Most of the Lua core runtime/ modules are precompiled to bytecode, so changes to those files won't get used unless you rebuild Nvim or by passing --luamod-dev and $VIMRUNTIME. For example, try adding a function to runtime/lua/vim/_editor.lua then:

VIMRUNTIME=./runtime ./build/bin/nvim --luamod-dev

Documentation

Read :help dev-doc to understand the expected documentation style and conventions.

Generating :help

Many :help docs are autogenerated from (C or Lua) docstrings. To generate the documentation run:

make doc

To validate the documentation files, run:

make lintdoc

If you need to modify or debug the documentation flow, these are the main files:

  • ./scripts/gen_vimdoc.lua: Main doc generator. Parses C and Lua files to render vimdoc files.

  • ./scripts/luacats_parser.lua: Documentation parser for Lua files.

  • ./scripts/cdoc_parser.lua: Documentation parser for C files.

  • ./scripts/luacats_grammar.lua: Lpeg grammar for LuaCATS

  • ./scripts/cdoc_grammar.lua: Lpeg grammar for C doc comments

  • ./scripts/gen_eval_files.lua: Generates documentation and Lua type files from metadata files:

    runtime/lua/vim/*     =>  runtime/doc/lua.txt
    runtime/lua/vim/*     =>  runtime/doc/lua.txt
    runtime/lua/vim/lsp/  =>  runtime/doc/lsp.txt
    src/nvim/api/*        =>  runtime/doc/api.txt
    src/nvim/eval.lua     =>  runtime/doc/builtin.txt
    src/nvim/options.lua  =>  runtime/doc/options.txt
    
  • ./scripts/lintdoc.lua: Validation and linting of documentation files.

Lua docstrings

Use LuaLS annotations in Lua docstrings to annotate parameter types, return types, etc. See :help dev-lua-doc.

  • The template for function documentation is:
    --- {Brief}
    ---
    --- {Long explanation}
    ---
    --- @param arg1 type {description}
    --- @param arg2 type {description}
    --- ...
    ---
    --- @return type {description}
    
  • If possible, add type information (table, string, number, ...). Multiple valid types are separated by a bar (string|table). Indicate optional parameters via type|nil.
  • If a function in your Lua module should not be documented, add @nodoc.
  • If the function is internal or otherwise non-public add @private. - Private functions usually should be underscore-prefixed (named "_foo", not "foo").
  • Mark deprecated functions with @deprecated.

Third-party dependencies

To build Nvim using a different commit of a dependency change the appropriate URL in cmake.deps/deps.txt. For example, to use a different version of luajit replace the value in LUAJIT_URL with the wanted commit hash:

LUAJIT_URL https://github.com/LuaJIT/LuaJIT/archive/<sha>.tar.gz

Set DEPS_IGNORE_SHA to TRUE in cmake.deps/CMakeLists.txt to skip hash check from cmake.

Alternatively, you may point the URL as a local path where the repository is. This is convenient when bisecting a problem in a dependency with git bisect. This may require running make distclean between each build. Hash checking is always skipped in this case regardless of DEPS_IGNORE_SHA.

LUAJIT_URL /home/user/luajit

Reviewing

Reviewing can be done on GitHub, but you may find it easier to do locally. Using GitHub CLI, you can create a new branch with the contents of a pull request, e.g. #1820:

gh pr checkout https://github.com/neovim/neovim/pull/1820

Use git log -p master..FETCH_HEAD to list all commits in the feature branch which aren't in the master branch; -p shows each commit's diff. To show the whole surrounding function of a change as context, use the -W argument as well.