Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 34 additions & 2 deletions doc/languages-frameworks/vim.section.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,16 +234,48 @@ Finally, there are some plugins that are also packaged in nodePackages because t

### Testing Neovim plugins {#testing-neovim-plugins}

`nvimRequireCheck=MODULE` is a simple test which checks if Neovim can requires the lua module `MODULE` without errors. This is often enough to catch missing dependencies.
#### neovimRequireCheck {#testing-neovim-plugins-neovim-require-check}
`neovimRequireCheck` is a simple test which checks if Neovim can requires lua modules without errors. This is often enough to catch missing dependencies.

This can be manually added through plugin definition overrides in the [overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/editors/vim/plugins/overrides.nix).
It accepts a single string for a module, or a list of module strings to test.
- `nvimRequireCheck = MODULE;`
- `nvimRequireCheck = [ MODULE1 MODULE2 ];`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My personal preference would have been to accept juste one format to avoid surprising the user "it's a list here but not there ?", plus it makes implementation easier. Like always accept a list and mark it plural:
nvimRequireChecks = []


When `neovimRequireCheck` is not specified, we will search the plugin's directory for lua modules to attempt loading. This quick smoke test can catch obvious dependency errors that might be missed.
The check hook will fail the build if any failures are detected to encourage inspecting the logs to identify potential issues.

If you would like to only check a specific module, this can be manually added through plugin definition overrides in the [overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/editors/vim/plugins/overrides.nix).

```nix
gitsigns-nvim = super.gitsigns-nvim.overrideAttrs {
dependencies = [ self.plenary-nvim ];
nvimRequireCheck = "gitsigns";
};
```
Some plugins will have lua modules that require a user configuration to function properly or can contain optional lua modules that we dont want to test requiring.
We can skip specific modules using `nvimSkipModule`. Similar to `nvimRequireCheck`, it accepts a single string or a list of strings.
- `nvimSkipModule = MODULE;`
- `nvimSkipModule = [ MODULE1 MODULE2 ];`

```nix
asyncrun-vim = super.asyncrun-vim.overrideAttrs {
nvimSkipModule = [
# vim plugin with optional toggleterm integration
"asyncrun.toggleterm"
"asyncrun.toggleterm2"
];
};
```

In rare cases, we might not want to actually test loading lua modules for a plugin. In those cases, we can disable `neovimRequireCheck` with `doCheck = false;`.

This can be manually added through plugin definition overrides in the [overrides.nix](https://github.com/NixOS/nixpkgs/blob/master/pkgs/applications/editors/vim/plugins/overrides.nix).
```nix
vim-test = super.vim-test.overrideAttrs {
# Vim plugin with a test lua file
doCheck = false;
};
```

### Plugin optional configuration {#vim-plugin-required-snippet}

Expand Down
3 changes: 3 additions & 0 deletions doc/redirects.json
Original file line number Diff line number Diff line change
Expand Up @@ -3760,6 +3760,9 @@
"testing-neovim-plugins": [
"index.html#testing-neovim-plugins"
],
"testing-neovim-plugins-neovim-require-check": [
"index.html#testing-neovim-plugins-neovim-require-check"
],
"vim-plugin-required-snippet": [
"index.html#vim-plugin-required-snippet"
],
Expand Down
138 changes: 125 additions & 13 deletions pkgs/applications/editors/vim/plugins/neovim-require-check-hook.sh
Original file line number Diff line number Diff line change
@@ -1,24 +1,136 @@
#shellcheck shell=bash
# Setup hook for checking whether Python imports succeed
# Setup hook for checking whether Lua imports succeed
echo "Sourcing neovim-require-check-hook.sh"

neovimRequireCheckHook () {
echo "Executing neovimRequireCheckHook"
# Discover modules automatically if nvimRequireCheck is not set
discover_modules() {
echo "Running module discovery in source directory..."

# Create unique lists so we can organize later
modules=()

if [ -n "$nvimRequireCheck" ]; then
echo "Check whether the following module can be imported: $nvimRequireCheck"
while IFS= read -r lua_file; do
# Ignore certain infra directories
if [[ "$lua_file" =~ debug/|scripts?/|tests?/|spec/ || "$lua_file" =~ .*\meta.lua ]]; then
continue
# Ignore optional telescope and lualine modules
elif [[ "$lua_file" =~ ^lua/telescope/_extensions/(.+)\.lua || "$lua_file" =~ ^lua/lualine/(.+)\.lua ]]; then
continue
# Grab main module names
elif [[ "$lua_file" =~ ^lua/([^/]+)/init.lua$ ]]; then
echo "$lua_file"
modules+=("${BASH_REMATCH[1]}")
# Check other lua files
elif [[ "$lua_file" =~ ^lua/(.*)\.lua$ ]]; then
echo "$lua_file"
# Replace slashes with dots to form the module name
module_name="${BASH_REMATCH[1]//\//.}"
modules+=("$module_name")
elif [[ "$lua_file" =~ ^([^/.][^/]*)\.lua$ ]]; then
echo "$lua_file"
modules+=("${BASH_REMATCH[1]}")
fi
done < <(find "$src" -name '*.lua' | xargs -n 1 realpath --relative-to="$src")

# editorconfig-checker-disable
export HOME="$TMPDIR"
nvimRequireCheck=("${modules[@]}")
echo "Discovered modules: ${nvimRequireCheck[*]}"

local deps="${dependencies[*]}"
@nvimBinary@ -es --headless -n -u NONE -i NONE --clean -V1 \
--cmd "set rtp+=$out,${deps// /,}" \
--cmd "lua require('$nvimRequireCheck')"
if [ "${#nvimRequireCheck[@]}" -eq 0 ]; then
echo "No valid Lua modules found; skipping check"
return 1
fi
return 0
}

echo "Using neovimRequireCheckHook"
appendToVar preDistPhases neovimRequireCheckHook
# Run require checks on each module in nvimRequireCheck
run_require_checks() {
echo "Starting require checks"
check_passed=false
failed_modules=()
successful_modules=()

export HOME="$TMPDIR"
local deps="${dependencies[*]}"
local checks="${nativeBuildInputs[*]}"
set +e
for name in "${nvimRequireCheck[@]}"; do
local skip=false
for module in "${nvimSkipModule[@]}"; do
if [[ "$module" == "$name" ]]; then
echo "$name is in list of modules to not check. Skipping..."
skip=true
break
fi
done

if [ "$skip" = false ]; then
echo "Attempting to require module: $name"
if @nvimBinary@ -es --headless -n -u NONE -i NONE --clean -V1 \
--cmd "set rtp+=$out,${deps// /,}" \
--cmd "set rtp+=$out,${checks// /,}" \
--cmd "lua require('$name')"; then
check_passed=true
successful_modules+=("$name")
echo "Successfully required module: $name"
else
echo "Failed to require module: $name"
failed_modules+=("$name")
fi
fi
done
set -e
}

# Define color codes
GREEN="\033[0;32m"
RED="\033[0;31m"
NC="\033[0m" # No Color

# Print summary of the require checks
print_summary() {
echo -e "\n======================================================"
if [[ "$check_passed" == "true" ]]; then
echo -e "${GREEN}Require check succeeded for the following modules:${NC}"
for module in "${successful_modules[@]}"; do
echo -e " ${GREEN}- $module${NC}"
done
echo "All lua modules were checked."
else
echo -e "${RED}No successful require checks.${NC}"
fi

# Print any modules that failed with improved formatting and color
if [ "${#failed_modules[@]}" -gt 0 ]; then
echo -e "\n${RED}Require check failed for the following modules:${NC}"
for module in "${failed_modules[@]}"; do
echo -e " ${RED}- $module${NC}"
done
fi
echo "======================================================"

if [ "${#failed_modules[@]}" -gt 0 ]; then
return 1
fi
}

# Main entry point: orchestrates discovery, require checks, and summary
neovimRequireCheckHook() {
echo "Executing neovimRequireCheckHook"

if [ "${nvimRequireCheck[*]}" = "" ]; then
echo "nvimRequireCheck is empty; entering discovery mode"
# Auto-discovery mode
if ! discover_modules; then
echo "No modules found during discovery; exiting hook"
return
fi
else
echo "nvimRequireCheck is pre-populated; entering manual check mode"
fi

run_require_checks
print_summary
}

echo "Using neovimRequireCheckHook"
appendToVar preDistPhases neovimRequireCheckHook
11 changes: 11 additions & 0 deletions pkgs/applications/editors/vim/plugins/overrides.nix
Original file line number Diff line number Diff line change
Expand Up @@ -466,6 +466,13 @@ in

cmp-dictionary = super.cmp-dictionary.overrideAttrs (oa: {
nativeCheckInputs = oa.nativeCheckInputs ++ [ self.nvim-cmp ];
nvimSkipModule = [
# Test files
"cmp_dictionary.dict.external_spec"
"cmp_dictionary.dict.trie_spec"
"cmp_dictionary.lib.trie_spec"
"cmp_dictionary.lib.unknown_spec"
];
});

cmp-digraphs = super.cmp-digraphs.overrideAttrs (oa: {
Expand Down Expand Up @@ -2897,6 +2904,10 @@ in
"snacks.terminal"
"snacks.win"
"snacks.words"
"snacks.debug"
"snacks.scratch"
# Optional trouble integration
"trouble.sources.profiler"
];
};

Expand Down
9 changes: 8 additions & 1 deletion pkgs/applications/editors/vim/plugins/vim-utils.nix
Original file line number Diff line number Diff line change
Expand Up @@ -499,8 +499,15 @@ rec {
nativeBuildInputs =
oldAttrs.nativeBuildInputs or [ ]
++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
vimCommandCheckHook
vimGenDocHook
];

doCheck = oldAttrs.doCheck or true;

nativeCheckInputs =
oldAttrs.nativeCheckInputs or [ ]
++ lib.optionals (stdenv.buildPlatform.canExecute stdenv.hostPlatform) [
vimCommandCheckHook
# many neovim plugins keep using buildVimPlugin
neovimRequireCheckHook
];
Expand Down