neovim-require-check-hook: auto discover modules to require

This commit is contained in:
Austin Horstman 2024-10-29 21:23:41 -05:00
parent df7560310e
commit 6ebffd546a
No known key found for this signature in database
4 changed files with 171 additions and 17 deletions

View File

@ -234,9 +234,17 @@ 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 ];`
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 {
@ -244,6 +252,30 @@ This can be manually added through plugin definition overrides in the [overrides
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}

View File

@ -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"
],

View File

@ -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 () {
# 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=()
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")
nvimRequireCheck=("${modules[@]}")
echo "Discovered modules: ${nvimRequireCheck[*]}"
if [ "${#nvimRequireCheck[@]}" -eq 0 ]; then
echo "No valid Lua modules found; skipping check"
return 1
fi
return 0
}
# 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 [ -n "$nvimRequireCheck" ]; then
echo "Check whether the following module can be imported: $nvimRequireCheck"
# editorconfig-checker-disable
export HOME="$TMPDIR"
local deps="${dependencies[*]}"
@nvimBinary@ -es --headless -n -u NONE -i NONE --clean -V1 \
--cmd "set rtp+=$out,${deps// /,}" \
--cmd "lua require('$nvimRequireCheck')"
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

View File

@ -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
];