Skip to content

nvim: add efm-langserver for linting - #5

Merged
kriswill merged 10 commits into
mainfrom
add-efm-linting
Apr 25, 2026
Merged

nvim: add efm-langserver for linting#5
kriswill merged 10 commits into
mainfrom
add-efm-linting

Conversation

@kriswill

Copy link
Copy Markdown
Owner

Summary

Phase 3 of the lazy.nvim → vim.pack migration — adds the lint layer that was missing.

  • New LSP efm wired via creativenull/efmls-configs-nvim, lint-only
  • Linters: markdownlint (markdown), hadolint (Dockerfile), yamllint (yaml), gitlint (gitcommit)
  • Shell linting stays with bash-language-server's built-in shellcheck to avoid duplicate SC#### diagnostics
  • Conform unchanged — still authoritative for all formatting
  • Nix: adds efm-langserver to lsp-servers, new linters group with the four CLI tools (plus shellcheck, which bashls uses)

Notable

The efmls-configs yamllint module ships without lintFormats or lintIgnoreExitCode. Without them efm drops all yamllint output (it exits 0 on warnings-only). This config overrides both.

Test plan

  • nix build .#darwinConfigurations.k.system — builds
  • sudo darwin-rebuild switch --flake . — applies
  • nvim --headless — clean startup, efmls-configs-nvim installed via vim.pack
  • Each target filetype opens + save → efm attaches + produces diagnostics:
    • markdown (MD041 missing H1)
    • Dockerfile (DL3007 + DL3018)
    • yaml (yamllint document-start)
    • gitcommit (gitlint B1 long line)
  • shell file → single SC2086 (no duplicate from efm)
  • conform regression: saving a lua file still runs stylua

kriswill added 10 commits April 24, 2026 12:35
Adds a lint layer for filetypes lacking one (markdown, Dockerfile,
yaml, gitcommit) using efm-langserver as an umbrella LSP, wired via
creativenull/efmls-configs-nvim. Conform stays authoritative for
formatting. Shell linting stays with bash-language-server's built-in
shellcheck integration to avoid duplicate SC#### diagnostics.

Overrides the plugin's yamllint module to add the missing lintFormats
and lintIgnoreExitCode; without them efm drops yamllint output since
yamllint exits 0 on warnings-only.
Disables bash-language-server's internal shellcheck via
bashIde.shellcheckPath = "" and adds shellcheck back to efm for
sh/bash/zsh, making efm the single source for SC#### diagnostics.
efm-langserver now handles formatting for every filetype conform used
to cover: html/js/ts/yaml/markdown (prettier_d), lua (stylua), nix
(nixfmt), python (isort+black), rust (rustfmt), sh/bash/zsh (shfmt),
xml (xmllint). Format-on-save is wired via a BufWritePre autocmd in
lua/config/lsp.lua that filters to client.name == "efm", so other
LSPs with formatting capability (vtsls/rust-analyzer/gopls/lua-ls)
don't compete.

Nix: adds rustfmt and libxml2 (for xmllint) to extraPackages so efm
can find them on nvim's PATH.

The <leader>cf keymap now calls vim.lsp.buf.format with the same
efm-only filter. conform.nvim is removed from the plugin list; its
leftover log pattern is dropped from baleia.nvim.
- yaml: swap prettier_d -> yamlfmt (inline config, rootMarkers look
  for .yamlfmt so project-local indent/rules apply on save)
- javascript/typescript/jsx/tsx: swap prettier_d -> biome; narrow
  rootMarkers to biome.json/biome.jsonc/rome.json so a stray
  package.json doesn't enroll non-biome projects
- shfmt: rootMarkers now includes .editorconfig so per-project shell
  indent rules take effect (prettier, stylua, isort, nixfmt already
  pick up their respective config files via the plugin defaults)
- Nix: add biome to extraPackages

Verified project-config pickup end-to-end:
- .yamlfmt indent=4 -> yamlfmt output uses 4-space indent
- .editorconfig indent_size=4 -> shfmt output uses 4-space indent
- .prettierrc printWidth=20 + proseWrap=always -> prettier wraps prose
Documents every enabled LSP (binary, config file, filetypes) and the
efm lint/format matrix (linter, formatter, rootMarkers per filetype),
plus the format-on-save filter and the extraPackages PATH caveat.
Previously EDITOR/VISUAL/MANPAGER were set to lib.getExe pkgs.neovim,
which is the generic nixpkgs wrapper and does NOT include
programs.neovim.extraPackages on PATH. Callers that go through
$EDITOR (yazi, git commit, crontab, etc.) launched an nvim without
access to lua-language-server, efm-langserver, etc., so no LSP
attached. Switching to config.programs.neovim.finalPackage resolves
to home-manager's own wrapper, which prepends extraPackages to PATH.
Result of the first save-formatted-by-efm pass on this file now that the
efm/prettier_d markdown pipeline is working end-to-end (verified by
opening from yazi, where extraPackages PATH had been missing before
70aeecf). Pure column-width reflow + one *…* → _…_ emphasis swap; no
semantic content changes.
The vim.pack migration (267dd6f) replaced lazy.nvim with native
vim.pack; the lockfile has been unused since then. NVIM_PACK.md already
lists it under "Removed" — this just makes the working tree match.
rumdl handles both lint and format in a single binary, dropping
markdownlint-cli from Nix and wiring two inline efm configs
(efmls-configs-nvim has no rumdl module) for stdin-based lint
and format-on-save.
Remove filename/alias strings from LSP filetypes arrays that nvim
doesn't recognize (.zshrc, javascript.jsx, typescript.tsx, opentofu,
opentofu-vars). Add ftplugin stubs so getcompletion() validates the
remaining custom filetypes (gowork, gotmpl, templ, terraform-vars,
yaml.docker-compose). Add config.filetypes with vim.filetype.add()
for runtime detection of those same types.
@kriswill
kriswill merged commit 82c5a5c into main Apr 25, 2026
@kriswill
kriswill deleted the add-efm-linting branch April 25, 2026 23:38
kriswill added a commit that referenced this pull request Jun 29, 2026
nix-darwin has no programs.* for most of these tools, so port them the way
the rest of the repo already does (cf. tmux/zsh/yazi): static config lives in
the stow tree (home/) and the /nix/store-derived bits are generated and linked
during activation. The shell integrations (fzf/zoxide/direnv/hstr) already live
in the stow zshrc, so only the FZF_* env vars needed re-adding there.

Moved out of modules/home-manager/core.nix (now deleted) into system-level
modules/darwin/<feature>.nix + home/<pkg> stow packages:

  git/gh      modules/darwin/git.nix         + home/git, home/gh (bare-name
                                                helpers, no stale store paths)
  ssh         modules/darwin/ssh.nix         + home/ssh
  zk          modules/darwin/zk.nix
  diffnav     modules/darwin/diffnav.nix     installs a delta wrapped with the
                                              kanagawa theme (matches HM's
                                              finalPackage; diffnav bundles its
                                              own delta and is unaffected)
  kitty       modules/darwin/kitty.nix       + home/kitty
  neovide     modules/darwin/neovide.nix     toggle only; pkg is per-host
  direnv      modules/darwin/direnv.nix      + home/direnv; nix-direnv stdlib
                                              linked into ~/.config/direnv/lib
  direnv-nom  modules/darwin/direnv-nom.nix  nom wrapper generated + linked
  htop        modules/darwin/htop.nix        immutable htoprc generated + linked
  qmd-sqlite  modules/darwin/qmd-sqlite.nix  extension-enabled sqlite (hiPrio so
                                              it wins the sqlite3 collision with
                                              neovim's plain sqlite) + qmd link
  bat jq nix-index lazygit rmpc fzf go nodejs_24 yamlfmt -> user-packages.nix
  FZF_* env vars -> home/zsh/.config/zsh/.zshrc

The home-manager master `kriswill.enable` toggle and core.nix were removed;
home-manager stays wired only for the GUI/per-host modules (brave, firefox,
vscode, podman-desktop, claude-account-selector). All three host configs build
(nix flake check) and the HM->stow config handoff is clean (the old HM configs
were store symlinks HM removes on switch, then stow/activation redeploys).

flake.lock: `nix flake update` bumps nixpkgs, home-manager and yazi-plugins.
This is a closure no-op for host k -- the new lock evaluates to the identical
darwin-system derivation (verified: same .drv d15di5x4..., same output
axiznxg2...). It is included so future builds and the other hosts track the
newer inputs.

nvd diff -- previous generation (system-184, pre-migration) -> this commit's
closure (== the running gen-185 system). The Selection-state / Added / Removed
sections are this port (note the hm_* generated configs removed and the
delta-wrapped/delta-config -> delta-kanagawa swap); the Version changes are the
already-committed af761b8 nixpkgs bump that the pre-port generation had not yet
realized.

<<< /nix/store/7kx6ii60i6fwp4gsn4l987r9klgwd909-darwin-system-26.11.a1fa429
>>> /nix/store/axiznxg2fgby564shlpxjkjp208wa6p8-darwin-system-26.11.a1fa429
Version changes:
[U*]  #1  buf                           1.70.0 -> 1.71.0
[U.]  #2  expat                         2.8.0 -> 2.8.1
[U*]  #3  fastfetch                     2.64.2, 2.64.2-man -> 2.65.1, 2.65.1-man
[U.]  #4  ffmpeg-headless               8.1-bin, 8.1-data, 8.1-lib -> 8.1.1-bin, 8.1.1-data, 8.1.1-lib
[U.]  #5  fftw-double                   3.3.10 -> 3.3.11
[U*]  #6  fish                          4.7.1, 4.7.1-doc -> 4.8.0, 4.8.0-doc
[U.]  #7  freetype                      2.14.2 -> 2.14.3
[U+]  #8  gh                            2.94.0 -> 2.95.0
[U*]  #9  ghostscript                   10.07.0, 10.07.0-fonts, 10.07.0-man -> 10.07.1, 10.07.1-fonts, 10.07.1-man
[U*]  #10  go                            1.26.3 -> 1.26.4
[U.]  #11  icu4c                         76.1, 76.1-dev -> 78.3, 78.3-dev
[U.]  #12  ijs                           10.07.0 -> 10.07.1
[U*]  #13  imagemagick                   7.1.2-23 -> 7.1.2-24
[U.]  #14  just                          1.51.0, 1.51.0-man -> 1.54.0, 1.54.0-man
[U.]  #15  krb5                          1.22.1-lib -> 1.22.2-lib
[U.]  #16  libde265                      1.0.18 -> 1.1.1
[U.]  #17  libgcrypt                     1.11.2-lib -> 1.12.2-lib
[U.]  #18  libheif                       1.21.2-lib -> 1.23.0-lib
[U.]  #19  libpng-apng                   1.6.56 -> 1.6.58
[U*]  #20  libxml2                       2.15.2, 2.15.2-bin -> 2.15.3, 2.15.3-bin
[U*]  #21  lua-language-server           3.18.1 -> 3.18.2
[D.]  #22  nix                           2.34.7+1 x2 -> 2.34.7 x2
[D.]  #23  nix-cmd                       2.34.7+1 -> 2.34.7
[D.]  #24  nix-expr                      2.34.7+1 -> 2.34.7
[D.]  #25  nix-fetchers                  2.34.7+1 -> 2.34.7
[D.]  #26  nix-flake                     2.34.7+1 -> 2.34.7
[D.]  #27  nix-main                      2.34.7+1 -> 2.34.7
[D.]  #28  nix-store                     2.34.7+1 -> 2.34.7
[D.]  #29  nix-util                      2.34.7+1 -> 2.34.7
[U*]  #30  nodejs                        24.15.0 -> 24.16.0
[C.]  #31  nodejs-slim                   22.22.3, 24.15.0, 24.15.0-corepack, 24.15.0-npm -> 22.23.1, 24.16.0, 24.16.0-corepack, 24.16.0-npm
[U.]  #32  openapv                       0.2.1.2 -> 0.2.1.3
[U.]  #33  openexr                       3.4.10 -> 3.4.11
[U.]  #34  podman                        5.8.2, 5.8.2-man -> 5.8.3, 5.8.3-man
[U.]  #35  publicsuffix-list             0-unstable-2026-03-26 -> 0-unstable-2026-05-13
[U.]  #36  rsync                         3.4.1 -> 3.4.4
[U*]  #37  rumdl                         0.2.16 -> 0.2.21
[U.]  #38  simdjson                      4.6.0 -> 4.6.4
[C*]  #39  sqlite                        3.51.2 x2, 3.51.2-bin x2, 3.51.2-dev, 3.51.2-man x2 -> 3.51.2 x2, 3.51.2-bin x2, 3.51.2-dev, 3.51.2-man
[U.]  #40  unbound                       1.25.0-lib -> 1.25.1-lib
[U.]  #41  uv                            0.11.19 -> 0.11.22
[D*]  #42  vscode-langservers-extracted  4.10.0 -> 1.121.03429
Selection state changes:
[C+]  #1  direnv   2.37.1
[C+]  #2  git-lfs  3.7.1
[C+]  #3  htop     3.5.1, 3.5.1-man
[C+]  #4  kitty    0.47.4, 0.47.4-terminfo
Added packages:
[A+]  #1  delta-kanagawa                                   <none>
[A.]  #2  delta-kanagawa.gitconfig                         <none>
[A.]  #3  fastfetch-unwrapped                              2.65.1, 2.65.1-man
[A.]  #4  home-manager-agent-domains                       <none>
[A.]  #5  org.nix-community.home.claude-config-dir.domain  <none>
[A.]  #6  zz-nom-wrapper.sh                                <none>
Removed packages:
[R.]  #1  delta-config                 <none>
[R.]  #2  delta-wrapped                <none>
[R.]  #3  direnv-config                <none>
[R.]  #4  empty-directory              <none>
[R.]  #5  gh-config.yml                <none>
[R.]  #6  hm_.sshconfig                <none>
[R.]  #7  hm_direnvlibzznomwrapper.sh  <none>
[R.]  #8  hm_gitallowed_signers        <none>
[R.]  #9  hm_gitconfig                 <none>
[R.]  #10  hm_gitignore                 <none>
[R.]  #11  hm_kanagawa.conf             <none>
[R.]  #12  hm_kittydiff.conf            <none>
[R.]  #13  hm_kittykitty.conf           <none>
[R.]  #14  yyjson                       0.12.0
Closure size: 624 -> 616 (611 paths added, 619 paths removed, delta -8, disk usage -53.1MiB).
kriswill added a commit that referenced this pull request Jun 29, 2026
…abled modules

Continues the home-manager retirement. The two remaining *enabled* HM modules
move to system-level darwin modules; the three disabled, unused ones are
deleted. modules/home-manager/ is now empty (the bridge in modules/home.nix and
the home-manager flake input are kept, per request, so HM still supplies
hm-session-vars.sh / TERMINFO_DIRS).

- podman-desktop -> modules/darwin/podman-desktop.nix (toggle) + the
  home/podman-desktop stow tree. The two mkOutOfStoreSymlink configs
  (containers.conf and the GUI app's settings.json) move into the stow tree;
  the normalize-podman-settings git filter follows in .gitattributes. enable
  moves to the darwin block on hosts k and SOC.
- claude-account-selector -> modules/darwin/claude-account-selector/. The
  generated zsh snippet (host rules prepended to wrapper.zsh) is built with
  writeText and linked during activation -- byte-identical to the old HM output
  -- ccglass is installed system-wide, and the desktop-app pin uses nix-darwin
  launchd.user.agents in place of home-manager's launchd.agents. Config moves to
  the darwin kriswill block on k (literal paths replace config.home.homeDirectory).
- brave / firefox / vscode deleted: disabled and unused, and they rely on
  home-manager's programs.* generators that nix-darwin has no equivalent for
  (recoverable from git history if ever wanted).
- modules/home.nix guards `config.flake.modules.homeManager or { }` so it still
  resolves now that flake-parts' lazyAttrsOf drops the empty homeManager class.

nix flake check passes for k, mini, SOC. The generated claude snippet and the
podman configs were verified identical to the home-manager output before switch.

nvd diff -- previous generation (system-185) -> this commit (system-186, now
active). The hm_* removals are the podman configs and the claude wrapper snippet
moving off home-manager; ccglass moves from the HM per-user profile into the
system profile; the launchd agent is re-created under the org.nixos.* label.

<<< /nix/store/axiznxg2fgby564shlpxjkjp208wa6p8-darwin-system-26.11.a1fa429
>>> /run/current-system
Selection state changes:
[C+]  #1  ccglass  1.0.0
Added packages:
[A.]  #1  claude-account-selector.zsh        <none>
[A.]  #2  org.nixos.claude-config-dir.plist  <none>
Removed packages:
[R.]  #1  hm_containers.conf                               <none>
[R.]  #2  hm_podmandesktopsettings.json                    <none>
[R.]  #3  hm_zshclaudeaccountselector.zsh                  <none>
[R.]  #4  org.nix-community.home.claude-config-dir.domain  <none>
[R.]  #5  org.nix-community.home.claude-config-dir.plist   <none>
Closure size: 616 -> 613 (18 paths added, 21 paths removed, delta -3, disk usage -1.1KiB).
kriswill added a commit that referenced this pull request Jun 29, 2026
Completes the migration. The home-manager bridge, flake input, and the last
runtime hook are gone; the repo is now pure nix-darwin + the GNU Stow tree under
home/.

- flake.nix: drop the home-manager input (flake.lock: removed the home-manager
  and home-manager/nixpkgs nodes).
- modules/home.nix deleted -- the darwin <- home-manager bridge.
- modules/darwin/core.nix: drop pkgs.home-manager from environment.systemPackages.
- home/zsh/.config/zsh/.zshrc: drop the hm-session-vars.sh source line.
- AGENTS.md / CLAUDE.md updated to describe a darwin-only repo (no home.nix, no
  modules/home-manager/, no mkOutOfStoreSymlink mechanism).

Terminfo is unaffected -- the one real risk. nix-darwin's own set-environment
already exports the full TERMINFO_DIRS (the Ghostty.app bundle, the per-user and
system nix profiles, and /usr/share/terminfo), so xterm-kitty (in the system
profile's share/terminfo) and xterm-ghostty (Ghostty's bundle) still resolve
without the home-manager session-vars line. Verified with infocmp.

nix flake check passes for k, mini, SOC.

nvd diff -- previous generation (system-186, home-manager present) -> this commit
(system-187, now active). Removes the entire home-manager apparatus plus the deps
nothing else in the closure used (man-db, groff, diffutils, inetutils,
libpipeline).

<<< /nix/store/gf8hnm6w88ng2kajiy4x5n29k55nq0wz-darwin-system-26.11.a1fa429
>>> /run/current-system
Removed packages:
[R.]  #1  activation-k                             <none>
[R.]  #2  check-link-targets.sh                    <none>
[R.]  #3  cleanup                                  <none>
[R.]  #4  diffutils                                3.12
[R.]  #5  groff                                    1.24.1
[R.]  #6  hm-modules-messages                      <none>
[R.]  #7  hm-session-vars.sh                       <none>
[R.]  #8  hm_LibraryFonts.homemanagerfontsversion  <none>
[R.]  #9  hm_Usersk.cache.keep                     <none>
[R.]  #10  hm_Usersk.localstate.keep                <none>
[R-]  #11  home-manager                             0-unstable-2026-04-24
[R.]  #12  home-manager-agent-domains               <none>
[R.]  #13  home-manager-agents                      <none>
[R.]  #14  home-manager-applications                <none>
[R.]  #15  home-manager-files                       <none>
[R.]  #16  home-manager-fonts                       <none>
[R.]  #17  home-manager-generation                  <none>
[R.]  #18  home-manager-path                        <none>
[R.]  #19  home-manager-source                      <none>
[R.]  #20  home-manager.sh                          <none>
[R.]  #21  inetutils                                2.7
[R.]  #22  libpipeline                              1.5.8
[R.]  #23  link                                     <none>
[R.]  #24  man-db                                   2.13.1
[R.]  #25  nixos-option                             <none>
[R.]  #26  nixos-option.nix                         <none>
Closure size: 613 -> 587 (6 paths added, 32 paths removed, delta -26, disk usage -20.7MiB).
kriswill added a commit that referenced this pull request Jul 1, 2026
nixpkgs' podman derivation set meta.platforms = lib.platforms.linux, so it
refuses to evaluate on aarch64-darwin and breaks every rebuild that pulls it
into a profile. Rather than override the meta, package the official upstream
release binary.

pkgs/podman.nix fetches podman-remote-release-darwin_arm64.zip as a fixed-
output derivation (fetchzip) and installs the adhoc-signed Mach-O binaries
verbatim (dontFixup keeps the signature valid; deps are all system libs).
overlays/podman.nix replaces pkgs.podman with it, so the host package lists
need no changes.

Note: the upstream zip is the remote client only -- it does not bundle the
gvproxy/krunkit/libkrun helpers nixpkgs' podman propagated on darwin (see the
Removed packages below). `podman machine` user-mode networking may need
gvproxy provided separately.

nvd diff, previous generation -> current (195 -> 196). This generation switch
also carried the concurrent flake-lock bump and the codebase-memory-mcp rename,
committed separately:

    <<< /nix/var/nix/profiles/system-195-link
    >>> /nix/var/nix/profiles/system-196-link
    Version changes:
    [U*]  #1  cbm-tools            0.8.1 -> 0.8.1-nix
    [U*]  #2  codebase-memory-mcp  0.8.1 -> 0.8.1-nix
    [U.]  #3  podman               5.8.3, 5.8.3-man -> 6.0.0
    [U*]  #4  tmux                 3.6a, 3.6a-man -> 3.7, 3.7-man
    [U.]  #5  uv                   0.11.22 -> 0.11.25
    Removed packages:
    [R.]  #1  gvproxy                       0.8.9
    [R.]  #2  krunkit                       1.2.1
    [R.]  #3  libepoxy                      1.5.10
    [R.]  #4  libkrun-efi                   1.19.0
    [R.]  #5  podman-helper-binary-wrapper  <none>
    [R.]  #6  virglrenderer                 0.10.4d-krunkit
    Closure size: 590 -> 583 (14 paths added, 21 paths removed, delta -7, disk usage -14.1MiB).
kriswill added a commit that referenced this pull request Jul 3, 2026
New knowledge/nvim/ area covering the whole Neovim configuration:

- Core concepts: architecture (vim.pack dispatcher + load order),
  lsp (native vim.lsp + efm single-formatter), keymaps (leader
  namespace topology), options, filetypes (shebang detection).
- Per-plugin catalog: 23 enriched docs under nvim/plugins/, one per
  spec file, cross-linked into the graph.
- Decision records: native-vim-pack (lazy.nvim -> vim.pack, PR #4)
  and efm-umbrella-formatting (conform removal, PR #5), mined from
  commit history and the removed NVIM_PACK.md.

okf scaffold gains a neovim-plugins pass (stubs nvim/plugins/<name>.md
from lua/plugins/ specs: leading comment, trigger, deps, version pin).
Type registry adds Neovim Plugin + Neovim Config; modules/neovim.md
links the area; AGENTS.md and the knowledge-bundle skill mention the
new scaffold target. okf validate: 103 files, 0 errors, 0 warnings.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant