diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml new file mode 100644 index 0000000..cb71755 --- /dev/null +++ b/.github/workflows/check.yml @@ -0,0 +1,41 @@ +name: nix flake check + +on: + push: + branches: [master, main] + pull_request: + branches: [master, main] + workflow_dispatch: + +jobs: + flake-check: + name: nix flake check + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Nix + uses: DeterminateSystems/nix-installer-action@main + + - name: Magic Nix Cache + uses: DeterminateSystems/magic-nix-cache-action@main + + - name: nix flake check + run: nix flake check --no-build --show-trace + + - name: Build default package (jsvim) + run: nix build .#jsvim --no-link --print-out-paths + + build-language-packages: + name: build ${{ matrix.pkg }} + runs-on: ubuntu-latest + needs: flake-check + strategy: + fail-fast: false + matrix: + pkg: [jsvim, rustvim, zvim, jvim, sharpvim, rvim] + steps: + - uses: actions/checkout@v4 + - uses: DeterminateSystems/nix-installer-action@main + - uses: DeterminateSystems/magic-nix-cache-action@main + - run: nix build .#${{ matrix.pkg }} --no-link --print-out-paths diff --git a/CHANGELOG.md b/CHANGELOG.md index c2ea52a..ffbe7ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### BREAKING + +- The default flake output is now `jsvim`, not `nvim`. Use + `nix run .#jsvim` (or `nix build .#jsvim`). The on-disk binary is still + named `nvim` (and `vim`) via package aliases, so day-to-day shell usage + is unchanged. +- The legacy package definition formerly named `nvim` has been removed. + The example `nixCats` and `regularCats` packages are still present. + +### Added + +- **JS/TS DX overhaul (`jsvim`)**: + - ESLint LSP (vscode-eslint-language-server) with `codeActionOnSave`. + - JSON / CSS / HTML / Tailwind / Emmet / YAML LSPs. + - SchemaStore.nvim integration for `package.json`, `tsconfig.json`, + GitHub Actions, etc. + - Working `vscode-js-debug` DAP setup with launch configs for + `node`, `tsx`, `npm` script picker, `jest`, `vitest` and the + pre-existing attach configs. + - `neotest` with `neotest-jest` and `neotest-vitest` adapters + (`tn` / `tf` / `ts` / `to` / `tw`). + - `package-info.nvim` for inline npm version info in `package.json`. + - `nvim-ts-autotag` (auto-close JSX tags) and + `nvim-ts-context-commentstring` (correct JSX comments). + - `nvim-autopairs` (general). +- **LSP**: + - Inlay hints auto-enabled on `LspAttach` for clients that support them, + with `th` toggle. + - `vim.diagnostic.config` configured (rounded float, severity sort, + bullet virtual_text prefix). + - Language-aware `root_markers` (no more `{ '.git' }`-only). + - ts_ls inlay hints + code lens settings. +- **Imports**: `oi`/`ia`/`ir`/`if` now use the typed code-action + kinds (`source.organizeImports.ts` etc.) instead of the raw + `_typescript.organizeImports` command. +- **`:checkhealth jsvim`** reports node/npm/tsc/LSP/formatter/linter + availability and the attached LSP clients on the current buffer. +- **CI**: `.github/workflows/check.yml` runs `nix flake check` and builds + every language package on push/PR. + +### Changed + +- `flake.nix` collapsed: every language package is now built from a + single `mkLanguagePackage` helper in `nix/lib.nix`, dedup'ing ~600 lines. +- Each `nix/languages/*.nix` is the single source of truth for its + language: it now declares `formatters`, `linters`, `lspName`, + runtime deps, and (for js) `js-debug-path`. The lua side reads the + merged tables via `nixCats.extra("languageConfig.formatters")` / + `...linters`, so adding a language no longer requires touching + `lua/myLuaConf/format/init.lua` or `lua/myLuaConf/lint/init.lua`. +- `prettierd` is now wired up for `json`, `jsonc`, `html`, `css`, `scss`, + `less`, `yaml`, `markdown`, `graphql`, `vue`, `svelte` (in addition to + the existing js/ts/jsx/tsx). +- `eslint_d` is now wired up as the JS/TS linter via `nvim-lint`. +- `mason-lspconfig` non-Nix path migrated to v2 API + (`automatic_enable = false` + per-server `vim.lsp.enable`). +- AI / Claude Code is now opt-in per package; only `jsvim`, `nixCats` + and `regularCats` enable it. The previous global `general -> ai` + link in `extraCats` has been removed. +- Claude Code toggle moved from `` (not transmitted by most + terminals) to `ct`. +- `vim.o.clipboard` is no longer set globally; per-key `y`/`p` + bindings give explicit control without clobbering registers. + +### Fixed + +- `nvim-dap-js` no longer references the (non-existent in Nix) Mason + `node-debug2-adapter`. All `pwa-*` adapters share one server-mode + spawn of vscode-js-debug's `dapDebugServer.js`. +- ts_ls settings: removed the invalid `displayStringForProperties` / + `preferDisplayStringForProperties` keys; replaced with valid + `inlayHints` / `implementationsCodeLens` / `referencesCodeLens` / + `preferences` settings. +- Format-on-save no longer runs twice (removed the redundant + `BufWritePre *` autocmd that duplicated `format_on_save`). +- Replaced deprecated APIs: `vim.lsp.get_active_clients`, + `vim.api.nvim_buf_set_option`, `vim.api.nvim_win_set_option`, + `vim.api.nvim_buf_get_option`, `vim.lsp.buf.execute_command`, + `vim.diagnostic.goto_prev`/`goto_next`, `vim.highlight.on_yank`. + ### Added - **Documentation**: Comprehensive guides for installation, language packages, and adding new languages diff --git a/README.md b/README.md index a8a8c62..157d0ce 100644 --- a/README.md +++ b/README.md @@ -33,15 +33,14 @@ nix develop # or nix flake show to see available packages Each package provides a specialized Neovim configuration for a specific language ecosystem: -| Package | Language | LSP | Debugger | Formatter | Use Case | -|---------|----------|-----|----------|-----------|----------| -| **jsvim** | JavaScript/TypeScript | ts_ls | vscode-js-debug | prettier | Frontend/Node.js development | -| **jvim** | Java | jdtls | Built-in | - | Java application development | -| **sharpvim** | C# | OmniSharp | - | - | .NET development | +| Package | Language | LSPs | Debugger | Formatter / Linter | Use Case | +|---------|----------|------|----------|--------------------|----------| +| **jsvim** *(default)* | JavaScript / TypeScript | ts_ls, eslint, jsonls, cssls, html, tailwindcss, emmet, yamlls | vscode-js-debug | prettierd / eslint_d | Frontend / Node.js development | +| **rustvim** | Rust | rust-analyzer | CodeLLDB | rustfmt / clippy | Rust development | | **zvim** | Zig | zls | LLDB | zig fmt | Zig systems programming | -| **rustvim** | Rust | rust-analyzer | CodeLLDB | rustfmt | Rust development with clippy linting | -| **rvim** | R | languageserver | - | styler | R statistical analysis & data science | -| **nvim** | General | TypeScript-focused | Multi-lang | prettier | Default multi-language setup | +| **jvim** | Java | jdtls | (JDWP attach) | google-java-format | JVM development | +| **sharpvim** | C# | omnisharp | netcoredbg | csharpier | .NET development | +| **rvim** | R | r_language_server | - | styler / lintr | R statistical analysis | ### Using Language-Specific Packages @@ -95,16 +94,38 @@ nvim/ ## Key Features by Package -### JavaScript/TypeScript (jsvim) - -- **LSP**: TypeScript Language Server with full TypeScript/JavaScript support -- **Debugging**: vscode-js-debug with launch/attach configurations -- **Formatting**: Prettier/Prettierd for consistent code style -- **Features**: - - Node.js ESM and CommonJS debugging - - Chrome/Browser debugging - - Jest test debugging - - Process attachment capabilities +### JavaScript / TypeScript (`jsvim`) — default package + +- **LSPs**: + - `ts_ls` (typescript-language-server) with inlay hints, + `implementations` and `references` code lenses + - `eslint` (vscode-eslint-language-server) with `codeActionOnSave` + - `jsonls` + `yamlls` driven by `SchemaStore.nvim` + - `cssls`, `html`, `tailwindcss`, `emmet_language_server` +- **Debugging**: vscode-js-debug, run as a server-mode adapter shared by + every `pwa-*` config: + - Launch current file with `node` or `tsx` + - Launch via `npm` script (interactive picker reads `package.json`) + - Debug Jest test (current file) via `node_modules/.bin/jest` + - Debug Vitest test (current file) + - Attach to Node by default port (9229), custom port, or PID picker + - Attach to / launch Chrome +- **Formatting**: `prettierd` (with `prettier` fallback) for js, ts, jsx, + tsx, json, jsonc, html, css, scss, less, yaml, markdown, graphql, vue, + svelte. Run on save via `conform.nvim`, manual via `FF`. +- **Linting**: `eslint_d` via `nvim-lint` on `BufWritePost` / + `BufReadPost` / `InsertLeave`. +- **Tests**: `neotest` with `neotest-jest` and `neotest-vitest`: + `tn` (nearest), `tf` (file), `ts` (summary), + `to` (output), `tw` (watch). +- **Imports** (`oi` / `ia` / `ir` / `if`): organize, add missing, + remove unused, fix all — uses typed code-action kinds. +- **package.json** UX (`n*`): inline npm version info, update, + delete, install via `package-info.nvim`. +- **JSX**: `nvim-ts-autotag` (auto-close tags) and + `nvim-ts-context-commentstring` (correct comment tokens inside JSX). +- **Health check**: `:checkhealth jsvim` reports tooling availability and + attached LSP clients. ### Rust (rustvim) @@ -196,10 +217,10 @@ Press `FF` to manually format the current file. ### Linting -Code quality checks run on buffer write: -- JavaScript/TypeScript: ESLint (commented, can enable) -- Rust: Clippy (integrated with rust-analyzer) -- R: lintr +Code quality checks run on buffer write / read / insert-leave: +- JavaScript / TypeScript: `eslint_d` (via `nvim-lint`) +- Rust: clippy (integrated via rust-analyzer's `check.command`) +- R: `lintr` ## Customization @@ -218,27 +239,23 @@ Edit `lua/myLuaConf/opts_and_keys.lua` to customize: ### Creating a Custom Package -Edit flake.nix and add a new package definition: +Every language package is built with the `mkLanguagePackage` helper in +`nix/lib.nix`. To add a new language `foo`: + +1. Create `nix/languages/foo.nix` declaring `lspsAndRuntimeDeps`, + `formatters`, `linters`, `lspName`, `packageName`, `appName`, `logo`. +2. Add it to the `languageModules` set in `nix/lib.nix`. +3. Add a one-liner output in `flake.nix`: ```nix -customvim = { pkgs, ... }: { - settings = { - configDirName = "nvim"; - wrapRc = true; - }; - categories = { - markdown = true; - general = true; - rust = true; # Your desired language - # ... other categories - }; -}; +foovim = moxLib.mkLanguagePackage { language = "foo"; }; ``` -Then build it: -```bash -nix build .#customvim -``` +4. Optionally extend `lua/myLuaConf/LSPs/init.lua` with an + `if nixCats('foo') then servers.foo_ls = { ... } end` block. + +The lua side reads formatters/linters automatically from the merged +language tables passed via `nixCats.extra("languageConfig.*")`. ## Architecture diff --git a/docs/adding-languages.md b/docs/adding-languages.md index dd0fd98..01e10ae 100644 --- a/docs/adding-languages.md +++ b/docs/adding-languages.md @@ -32,39 +32,46 @@ Before starting, gather information about your language's development tools: ### Step 2: Create the Language Module -Create a new file `nix/languages/mylang.nix`: +Create a new file `nix/languages/mylang.nix`. The module is the SINGLE +SOURCE OF TRUTH for the language: its formatter / linter tables are +read straight into conform.nvim and nvim-lint at startup, so you +should never have to edit those lua files for a new language. ```nix -# Zig language configuration { pkgs }: { - # LSP server and runtime dependencies + ## Tools available on PATH inside the wrapped Neovim. + ## LSPs, formatters, linters AND the runtime they need (e.g. node) all + ## go here. lspsAndRuntimeDeps = with pkgs; [ my-language-server - # Add any required build tools + my-formatter + my-linter my-build-tool ]; - # Debug adapter (leave empty [] if not available) - debug = with pkgs; [ - my-debugger - # or: debug = []; - ]; + ## Debug adapters. Empty list if none. + debug = with pkgs; [ my-debugger ]; - # Code formatter - formatter = with pkgs; [ - my-formatter - ]; + ## conform.nvim formatters_by_ft contributions for this language. + ## The lua side merges every language's formatters into a single table. + formatters = { + mylang = [ "my-formatter" ]; + }; - # Linter name (optional) - linter = "my-linter"; + ## nvim-lint linters_by_ft contributions. + linters = { + mylang = [ "my-linter" ]; + }; + + ## Treesitter parsers (informational; the package uses withAllGrammars). + treesitter = [ "mylang" ]; - # Package naming packageName = "mylangvim"; - appName = "mylangvim"; + appName = "mylangvim"; + lspName = "my_language_server"; - # ASCII art logo (6 lines recommended, consistent with other logos) logo = '' ███╗ ███╗██╗ ██╗██╗ █████╗ ███╗ ██╗ ██████╗ ████╗ ████║╚██╗ ██╔╝██║ ██╔══██╗████╗ ██║██╔════╝ @@ -73,9 +80,6 @@ Create a new file `nix/languages/mylang.nix`: ██║ ╚═╝ ██║ ██║ ███████╗██║ ██║██║ ╚████║╚██████╔╝ ╚═╝ ╚═╝ ╚═╝ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═════╝ ''; - - # LSP server name (as used in lspconfig) - lspName = "my_language_server"; } ``` @@ -84,47 +88,48 @@ Create a new file `nix/languages/mylang.nix`: - Ensure all packages exist in nixpkgs (use `nix search nixpkgs my-formatter`) - For debuggers, prioritize standard ones: LLDB, GDB, or language-specific adapters -### Step 3: Update flake.nix +### Step 3: Register the module and create the package output -Add your language to the language definitions in `flake.nix`: +Two small edits. -#### In `categoryDefinitions`: +#### a) `nix/lib.nix` — add to `languageModules`: ```nix -mylang = (import ./nix/languages/mylang.nix { inherit pkgs; }).lspsAndRuntimeDeps; +languageModules = pkgs: { + ## ... existing entries ... + mylang = import ./languages/mylang.nix { inherit pkgs; }; +}; ``` -#### In `debugPkgs`: +#### b) `flake.nix` — wire the runtime deps and add the output: + +In `categoryDefinitions.lspsAndRuntimeDeps`: ```nix -mylang = (import ./nix/languages/mylang.nix { inherit pkgs; }).debug; +mylang = langs.mylang.lspsAndRuntimeDeps; ``` -#### In `extraCats`: +In `categoryDefinitions.lspsAndRuntimeDeps.debug`: ```nix -mylang = [ - [ "debug" "mylang" ] -]; +mylang = langs.mylang.debug; ``` -#### Create the package definition (use `mkLanguagePackage`): +In `categoryDefinitions.extraCats` (only if your language has a debug +adapter): ```nix -mylangvim = nixpkgs.legacyPackages.${system}.callPackage ./nixCatsBuilder.nix { - system = system; - categories = { - general = true; - LSPs = true; - mylang = true; - debug = (!(builtins.elem system disabledSystems)); - format = true; - lint = true; - }; - categoryDefinitions = categoryDefinitions; -}; +mylang = [ [ "debug" "mylang" ] ]; ``` +In `packageDefinitions`: + +```nix +mylangvim = moxLib.mkLanguagePackage { language = "mylang"; }; +``` + +That's it for nix — no further edits to format/lint lua files needed. + ### Step 4: Configure LSP Edit `lua/myLuaConf/LSPs/init.lua` and add your language server configuration: @@ -215,62 +220,24 @@ If a debugger is available, edit `lua/myLuaConf/debug/init.lua`: - `server`: Debug adapter running as a server - `pipe`: Communication via named pipes -### Step 6: Configure Formatting - -Edit `lua/myLuaConf/format/init.lua` and add to `formatters_by_ft`: - -```lua -mylang = { "my-formatter" }, -``` - -If your formatter needs special configuration, add to the `formatters` table: - -```lua -my_formatter = { - command = "my-formatter", - args = { "--option", "value", "--stdin" }, - stdin = true, -}, -``` - -**Common Patterns**: +### Step 6: Formatting (auto-wired) -```lua --- Simple formatter -rust = { "rustfmt" }, +Nothing to do in lua. The `formatters` table you declared in +`nix/languages/mylang.nix` is merged into conform.nvim's +`formatters_by_ft` automatically via +`nixCats.extra("languageConfig.formatters")`. --- Formatter with options -go = { - "gofmt", - extra_args = { "-w" }, -}, - --- Multiple formatters (priority order) -javascript = { "prettier", "eslint_d" }, -``` +If your formatter needs special invocation (e.g. running through a +runtime like `R --slave -e ...`), add a custom formatter definition to +the `formatters = { ... }` block in `lua/myLuaConf/format/init.lua` (see +the existing `styler` and `zigfmt` entries). -### Step 7: Configure Linting (Optional) +### Step 7: Linting (auto-wired) -Edit `lua/myLuaConf/lint/init.lua` and add: - -```lua -mylang = { 'my-linter' }, -``` - -If you need custom linter configuration: - -```lua -linters.my_linter = { - cmd = 'my-linter', - stdin = false, - args = { '--config', '.my-linter-rc' }, - stream = 'stdout', - ignore_exitcode = false, - parser = require('lint.parser').from_errorformat('%f:%l:%c: %m'), -} - -mylang = { 'my-linter' }, -``` +Same story: the `linters` table you declared in your language module is +the source of truth. nvim-lint reads it via +`nixCats.extra("languageConfig.linters")` and runs on `BufWritePost` / +`BufReadPost` / `InsertLeave`. ### Step 8: Test Your Configuration diff --git a/docs/installation.md b/docs/installation.md index 55b8a2e..ac68645 100644 --- a/docs/installation.md +++ b/docs/installation.md @@ -67,10 +67,14 @@ nix run .#zvim # R nix run .#rvim -# General Neovim (all features) -nix run .#nvim +# Default (same as `.#jsvim`) +nix run . ``` +> Note: the default flake output is `jsvim` (renamed from the old `nvim` +> output). The on-PATH binary is still called `nvim` (and `vim`) via +> package aliases. + ### Step 5: Build a Package To build a specific package and keep it for later use: @@ -82,7 +86,7 @@ nix build .#jsvim This creates a `result` symlink with the built package. Run it with: ```bash -./result/bin/nvim +./result/bin/jsvim # or `./result/bin/nvim` / `./result/bin/vim` ``` ## Installation Without Nix diff --git a/docs/language-packages.md b/docs/language-packages.md index 0f91caa..c22a03c 100644 --- a/docs/language-packages.md +++ b/docs/language-packages.md @@ -336,23 +336,19 @@ nvim analysis.R --- -## General Package (nvim) +## Default package (`jsvim`) -**Best for**: Multi-language development with all features enabled - -The `nvim` package includes: -- All language support (JavaScript, Java, C#, Rust, Zig, R) -- All plugins and features -- Telescope fuzzy finder -- Oil file manager -- Treesitter for all supported languages -- DAP debugging for all languages -- Formatting and linting for all languages +The default flake output is `jsvim` — a JavaScript / TypeScript-focused +package. There is no separate "general" package any more; if you want +multi-language support, build (or compose) your own with +`mkLanguagePackage` (see `nix/lib.nix`) or simply enable additional +language categories on top of `jsvim` via `extraCategories`. **Quick Start**: ```bash -nix run .#nvim +nix run . # same as `nix run .#jsvim` +nix run .#jsvim ``` --- diff --git a/flake.nix b/flake.nix index 8b9d792..af00d73 100644 --- a/flake.nix +++ b/flake.nix @@ -1,28 +1,20 @@ -# Multi-language Neovim configuration with nixCats -# Provides language-specific packages (jsvim, rustvim, rvim, zvim, jvim, sharpvim) -# All packages share the config directory "mox-nvim" for consistency -# To use separate config directories, override configDirName in package definitions +## Multi-language Neovim configuration with nixCats. +## +## Outputs (per system): +## .#jsvim JavaScript / TypeScript (default) +## .#rustvim Rust +## .#zvim Zig +## .#jvim Java +## .#sharpvim C# +## .#rvim R +## .#regularCats Impure dev variant (loads config from $XDG_CONFIG) +## .#nixCats Original example package, kept for reference +## +## All language packages share one configDirName ("mox-nvim"), one lua tree, +## and a single per-language module under nix/languages/. +## +## Originally based on the nixCats template by BirdeeHub. MIT licensed. -# Copyright (c) 2023 BirdeeHub -# Licensed under the MIT license - -# Welcome to the main example config of nixCats! -# there is a minimal flake the starter templates use -# within the nix directory without the nixpkgs input, -# but this one would work too! -# Every config based on nixCats is a full nixCats. - -# This example config doesnt use lazy.nvim, and -# it loads everything via nix. - -# It has some useful tricks -# in it, especially for lsps, so if you have any questions, -# first look through the docs, and then here! -# It has examples of most of the things you would want to do -# in your main nvim configuration. - -# If there is still not adequate info, ask in discussions -# on the nixCats repo (or open a PR to add the info to the help!) { description = "A Lua-natic's neovim flake, with extra cats! nixCats!"; @@ -32,147 +24,68 @@ url = "github:BirdeeHub/nixCats-nvim"; inputs.nixpkgs.follows = "nixpkgs"; }; - - # see :help nixCats.flake.inputs - # If you want your plugin to be loaded by the standard overlay, - # i.e. if it wasnt on nixpkgs, but doesnt have an extra build step. - # Then you should name it "plugins-something" - # If you wish to define a custom build step not handled by nixpkgs, - # then you should name it in a different format, and deal with that in the - # overlay defined for custom builds in the overlays directory. - # for specific tags, branches and commits, see: - # https://nixos.org/manual/nix/stable/command-ref/new-cli/nix3-flake.html#examples - - # No longer fetched to avoid forcing people to import it, but this remains here as a tutorial. - # How to import it into your config is shown farther down in the startupPlugins set. - # You put it here like this, and then below you would use it with `pkgs.neovimPlugins.hlargs` - - # "plugins-hlargs" = { - # url = "github:m-demare/hlargs.nvim"; - # flake = false; - # }; - - # neovim-nightly-overlay = { - # url = "github:nix-community/neovim-nightly-overlay"; - # }; - }; - # see :help nixCats.flake.outputs outputs = { self, nixpkgs, ... }@inputs: let inherit (inputs.nixCats) utils; luaPath = "${./.}"; - # this is flake-utils eachSystem forEachSystem = utils.eachSystem nixpkgs.lib.platforms.all; - # the following extra_pkg_config contains any values - # which you want to pass to the config set of nixpkgs - # import nixpkgs { config = extra_pkg_config; inherit system; } - # will not apply to module imports - # as that will have your system values - extra_pkg_config = { - # allowUnfree = true; - }; - # management of the system variable is one of the harder parts of using flakes. - - # so I have done it here in an interesting way to keep it out of the way. - # It gets resolved within the builder itself, and then passed to your - # categoryDefinitions and packageDefinitions. + extra_pkg_config = { }; - # this allows you to use ${pkgs.system} whenever you want in those sections - # without fear. + moxLib = import ./nix/lib.nix { inherit nixpkgs; }; - # sometimes our overlays require a ${system} to access the overlay. - # Your dependencyOverlays can either be lists - # in a set of ${system}, or simply a list. - # the nixCats builder function will accept either. - # see :help nixCats.flake.outputs.overlays - dependencyOverlays = /* (import ./overlays inputs) ++ */ [ - # This overlay grabs all the inputs named in the format - # `plugins-` - # Once we add this overlay to our nixpkgs, we are able to - # use `pkgs.neovimPlugins`, which is a set of our plugins. + dependencyOverlays = [ (utils.standardPluginOverlay inputs) - # add any other flake overlays here. - - # when other people mess up their overlays by wrapping them with system, - # you may instead call this function on their overlay. - # it will check if it has the system in the set, and if so return the desired overlay - # (utils.fixSystemizedOverlay inputs.codeium.overlays - # (system: inputs.codeium.overlays.${system}.default) - # ) ]; - # see :help nixCats.flake.outputs.categories - # and - # :help nixCats.flake.outputs.categoryDefinitions.scheme + ## ---------------------------------------------------------------- ## + ## categoryDefinitions + ## ---------------------------------------------------------------- ## + ## + ## All category contents are derived from the per-language modules + ## (nix/languages/*.nix) so that each language has exactly one source + ## of truth. categoryDefinitions = { pkgs, settings, categories, extra, name, mkNvimPlugin, ... }@packageDef: let - # Load language modules - langModules = { - javascript = import ./nix/languages/javascript.nix { inherit pkgs; }; - java = import ./nix/languages/java.nix { inherit pkgs; }; - csharp = import ./nix/languages/csharp.nix { inherit pkgs; }; - rust = import ./nix/languages/rust.nix { inherit pkgs; }; - zig = import ./nix/languages/zig.nix { inherit pkgs; }; - r = import ./nix/languages/r.nix { inherit pkgs; }; - }; + langs = moxLib.languageModules pkgs; in { - # to define and use a new category, simply add a new list to a set here, - # and later, you will include categoryname = true; in the set you - # provide when you build the package using this builder function. - # see :help nixCats.flake.outputs.packageDefinitions for info on that section. - - # lspsAndRuntimeDeps: - # this section is for dependencies that should be available - # at RUN TIME for plugins. Will be available to PATH within neovim terminal - # this includes LSPs lspsAndRuntimeDeps = { - # some categories of stuff. general = with pkgs; [ universal-ctags ripgrep fd ]; - # these names are arbitrary. - lint = with pkgs; [ - ]; - # but you can choose which ones you want - # per nvim package you export - debug = with pkgs; { - csharp = []; - go = [ delve ]; - js = langModules.javascript.debug; - java = []; - zig = langModules.zig.debug; - rust = langModules.rust.debug; - r = []; - }; - # Language-specific LSP and runtime dependencies (from modular language files) - js = langModules.javascript.lspsAndRuntimeDeps; - java = langModules.java.lspsAndRuntimeDeps; - csharp = langModules.csharp.lspsAndRuntimeDeps; - zig = langModules.zig.lspsAndRuntimeDeps; - rust = langModules.rust.lspsAndRuntimeDeps; - r = langModules.r.lspsAndRuntimeDeps; - # and easily check if they are included in lua + lint = with pkgs; [ ]; format = with pkgs; [ prettierd ]; + ## Per-language tools (LSPs, formatters, linters, runtimes). + js = langs.javascript.lspsAndRuntimeDeps; + java = langs.java.lspsAndRuntimeDeps; + csharp = langs.csharp.lspsAndRuntimeDeps; + zig = langs.zig.lspsAndRuntimeDeps; + rust = langs.rust.lspsAndRuntimeDeps; + r = langs.r.lspsAndRuntimeDeps; + ## Debug adapters per language. + debug = with pkgs; { + go = [ delve ]; + js = langs.javascript.debug; + java = langs.java.debug; + csharp = langs.csharp.debug; + zig = langs.zig.debug; + rust = langs.rust.debug; + r = langs.r.debug; + }; neonixdev = { - # also you can do this. inherit (pkgs) nix-doc lua-language-server nixd; - # and each will be its own sub category }; }; - # This is for plugins that will load at startup without using packadd: startupPlugins = { debug = with pkgs.vimPlugins; [ nvim-nio ]; general = with pkgs.vimPlugins; { - # you can make subcategories!!! - # (always isnt a special name, just the one I chose for this subcategory) always = [ lze vim-repeat @@ -180,71 +93,57 @@ ollama-nvim nvim-notify transparent-nvim - nerdtree + nerdtree ]; extra = [ oil-nvim nvim-web-devicons ]; }; - # You can retreive information from the - # packageDefinitions of the package this was packaged with. - # :help nixCats.flake.outputs.categoryDefinitions.scheme themer = with pkgs.vimPlugins; (builtins.getAttr (categories.colorscheme or "onedark") { - # Theme switcher without creating a new category - "onedark" = onedark-nvim; - "catppuccin" = catppuccin-nvim; - "catppuccin-mocha" = catppuccin-nvim; - "tokyonight" = tokyonight-nvim; - "tokyonight-day" = tokyonight-nvim; - } - ); - # This is obviously a fairly basic usecase for this, but still nice. + "onedark" = onedark-nvim; + "catppuccin" = catppuccin-nvim; + "catppuccin-mocha" = catppuccin-nvim; + "tokyonight" = tokyonight-nvim; + "tokyonight-day" = tokyonight-nvim; + }); }; - # not loaded automatically at startup. - # use with packadd and an autocommand in config to achieve lazy loading - # or a tool for organizing this like lze or lz.n! - # to get the name packadd expects, use the - # `:NixCats pawsible` command to see them all optionalPlugins = { debug = with pkgs.vimPlugins; { - # it is possible to add default values. - # there is nothing special about the word "default" - # but we have turned this subcategory into a default value - # via the extraCats section at the bottom of categoryDefinitions. default = [ nvim-dap nvim-dap-ui nvim-dap-virtual-text ]; - go = [ nvim-dap-go ]; - js = []; + go = [ nvim-dap-go ]; + js = []; csharp = []; - java = []; - zig = []; - rust = []; - r = []; + java = []; + zig = []; + rust = []; + r = []; }; - lint = with pkgs.vimPlugins; [ - nvim-lint - ]; - format = with pkgs.vimPlugins; [ - conform-nvim - ]; - markdown = with pkgs.vimPlugins; [ - markdown-preview-nvim - ]; - neonixdev = with pkgs.vimPlugins; [ - lazydev-nvim - ]; - ai = with pkgs.vimPlugins; [ - claude-code-nvim + lint = with pkgs.vimPlugins; [ nvim-lint ]; + format = with pkgs.vimPlugins; [ conform-nvim ]; + markdown = with pkgs.vimPlugins; [ markdown-preview-nvim ]; + neonixdev = with pkgs.vimPlugins; [ lazydev-nvim ]; + ai = with pkgs.vimPlugins; [ claude-code-nvim ]; + + ## JS-specific plugins. Activated only when the `js` category is on. + js = with pkgs.vimPlugins; [ + nvim-ts-autotag + nvim-ts-context-commentstring + package-info-nvim + SchemaStore-nvim + neotest + neotest-jest + neotest-vitest ]; + general = { cmp = with pkgs.vimPlugins; [ - # cmp stuff nvim-cmp luasnip friendly-snippets @@ -261,22 +160,13 @@ treesitter = with pkgs.vimPlugins; [ nvim-treesitter-textobjects nvim-treesitter.withAllGrammars - # This is for if you only want some of the grammars - # (nvim-treesitter.withPlugins ( - # plugins: with plugins; [ - # nix - # lua - # ] - # )) ]; telescope = with pkgs.vimPlugins; [ telescope-fzf-native-nvim telescope-ui-select-nvim telescope-nvim ]; - snacks = with pkgs.vimPlugins; [ - snacks-nvim - ]; + snacks = with pkgs.vimPlugins; [ snacks-nvim ]; always = with pkgs.vimPlugins; [ nvim-lspconfig lualine-nvim @@ -287,659 +177,156 @@ vim-rhubarb nvim-surround nvim-spectre + nvim-autopairs ]; extra = with pkgs.vimPlugins; [ fidget-nvim - # lualine-lsp-progress which-key-nvim comment-nvim undotree indent-blankline-nvim vim-startuptime - # If it was included in your flake inputs as plugins-hlargs, - # this would be how to add that plugin in your config. - # pkgs.neovimPlugins.hlargs ]; }; }; - # shared libraries to be added to LD_LIBRARY_PATH - # variable available to nvim runtime sharedLibraries = { - general = with pkgs; [ # <- this would be included if any of the subcategories of general are - # libgit2 - ]; + general = with pkgs; [ ]; }; - # environmentVariables: - # this section is for environmentVariables that should be available - # at RUN TIME for plugins. Will be available to path within neovim terminal environmentVariables = { test = { - default = { - CATTESTVARDEFAULT = "It worked!"; - }; - subtest1 = { - CATTESTVAR = "It worked!"; - }; - subtest2 = { - CATTESTVAR3 = "It didn't work!"; - }; + default = { CATTESTVARDEFAULT = "It worked!"; }; + subtest1 = { CATTESTVAR = "It worked!"; }; + subtest2 = { CATTESTVAR3 = "It didn't work!"; }; }; }; - # If you know what these are, you can provide custom ones by category here. - # If you dont, check this link out: - # https://github.com/NixOS/nixpkgs/blob/master/pkgs/build-support/setup-hooks/make-wrapper.sh extraWrapperArgs = { - test = [ - '' --set CATTESTVAR2 "It worked again!"'' - ]; + test = [ '' --set CATTESTVAR2 "It worked again!"'' ]; }; - # lists of the functions you would have passed to - # python.withPackages or lua.withPackages - - # get the path to this python environment - # in your lua config via - # vim.g.python3_host_prog - # or run from nvim terminal via :!-python3 - python3.libraries = { - test = (_:[]); - }; - # populates $LUA_PATH and $LUA_CPATH - extraLuaPackages = { - general = [ (_:[]) ]; - }; + python3.libraries = { test = (_:[]); }; + extraLuaPackages = { general = [ (_:[]) ]; }; - # see :help nixCats.flake.outputs.categoryDefinitions.default_values - # this will enable test.default and debug.default - # if any subcategory of test or debug is enabled - # WARNING: use of categories argument in this set will cause infinite recursion - # The categories argument of this function is the FINAL value. - # You may use it in any of the other sets. extraCats = { - test = [ - [ "test" "default" ] - ]; - debug = [ - [ "debug" "default" ] - ]; - csharp = [ - [ "debug" "csharp" ] - ]; - go = [ - [ "debug" "go" ] # yes it has to be a list of lists - ]; - js = [ - [ "debug" "js" ] - ]; - java = [ - [ "debug" "java" ] - ]; - zig = [ - [ "debug" "zig" ] - ]; - rust = [ - [ "debug" "rust" ] - ]; - r = [ - [ "debug" "r" ] - ]; - # Enable AI category when general is enabled (since all packages have general = true) - general = [ - [ "ai" ] - ]; + test = [ [ "test" "default" ] ]; + debug = [ [ "debug" "default" ] ]; + csharp = [ [ "debug" "csharp" ] ]; + go = [ [ "debug" "go" ] ]; + js = [ [ "debug" "js" ] ]; + java = [ [ "debug" "java" ] ]; + zig = [ [ "debug" "zig" ] ]; + rust = [ [ "debug" "rust" ] ]; + r = [ [ "debug" "r" ] ]; + ## NOTE: AI is now opt-in per package; no longer forced on by `general`. }; }; - - - - # packageDefinitions: - - # Now build a package with specific categories from above - # All categories you wish to include must be marked true, - # but false may be omitted. - # This entire set is also passed to nixCats for querying within the lua. - # It is directly translated to a Lua table, and a get function is defined. - # The get function is to prevent errors when querying subcategories. - - # see :help nixCats.flake.outputs.packageDefinitions + ## ---------------------------------------------------------------- ## + ## packageDefinitions + ## ---------------------------------------------------------------- ## packageDefinitions = { - # the name here is the name of the package - # and also the default command name for it. - nixCats = { pkgs, ... }@misc: { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions - settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `nixCats`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ "vim" "vimcat" ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "nixCats-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - }; - # enable the categories you want from categoryDefinitions - categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - ai = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the go category, - # and ALSO debug.go and debug.default due to our extraCats in categoryDefinitions. - # go = true; # <- disabled but you could enable it with override or module on install - - # this does not have an associated category of plugins, - # but lua can still check for it - lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - }; - extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; + jsvim = moxLib.mkLanguagePackage { + language = "javascript"; + aliases = [ "vim" "nvim" ]; + extraCategories = { ai = true; }; }; - regularCats = { pkgs, ... }@misc: { - settings = { - # IMPURE PACKAGE: normal config reload - # include same categories as main config, - # will load from vim.fn.stdpath('config') - wrapRc = false; - # or tell it some other place to load - # unwrappedCfgPath = "/some/path/to/your/config"; - # configDirName: will now look for nixCats-nvim within .config and .local and others - # this can be changed so that you can choose which ones share data folders for auths - # :h $NVIM_APPNAME - configDirName = "nixCats-nvim"; + jvim = moxLib.mkLanguagePackage { language = "java"; }; + sharpvim = moxLib.mkLanguagePackage { language = "csharp"; }; + zvim = moxLib.mkLanguagePackage { language = "zig"; }; + rustvim = moxLib.mkLanguagePackage { language = "rust"; }; + rvim = moxLib.mkLanguagePackage { language = "r"; }; - aliases = [ "testCat" ]; + ## ----- example packages preserved from the nixCats template ----- ## - # If you wanted nightly, uncomment this, and the flake input. - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - # Probably add the cache stuff they recommend too. - }; - categories = { - markdown = true; - general = true; - neonixdev = true; - lint = true; - format = true; - test = true; - # go = true; # <- disabled but you could enable it with override or module on install - lspDebugMode = false; - themer = true; - colorscheme = "catppuccin"; - }; - extra = { - # nixCats.extra("path.to.val") will perform vim.tbl_get(nixCats.extra, "path" "to" "val") - # this is different from the main nixCats("path.to.cat") in that - # the main nixCats("path.to.cat") will report true if `path.to = true` - # even though path.to.cat would be an indexing error in that case. - # this is to mimic the concept of "subcategories" but may get in the way of just fetching values. - nixdExtras = { - nixpkgs = nixpkgs; - }; - # yes even tortured inputs work. - theBestCat = "says meow!!"; - theWorstCat = { - thing'1 = [ "MEOW" '']]' ]=][=[HISSS]]"[['' ]; - thing2 = [ - { - thing3 = [ "give" "treat" ]; - } - "I LOVE KEYBOARDS" - (utils.n2l.types.inline-safe.mk ''[[I am a]] .. [[ lua ]] .. type("value")'') - ]; - thing4 = "couch is for scratching"; - }; - }; - }; - nvim = { pkgs, ... }@misc: - let - langModules = { - javascript = import ./nix/languages/javascript.nix { inherit pkgs; }; - java = import ./nix/languages/java.nix { inherit pkgs; }; - csharp = import ./nix/languages/csharp.nix { inherit pkgs; }; - rust = import ./nix/languages/rust.nix { inherit pkgs; }; - zig = import ./nix/languages/zig.nix { inherit pkgs; }; - r = import ./nix/languages/r.nix { inherit pkgs; }; - }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions + nixCats = { pkgs, ... }: { settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `nixCats`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ "vim" ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - }; - # enable the categories you want from categoryDefinitions - categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - ai = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the go category, - # and ALSO debug.go and debug.default due to our extraCats in categoryDefinitions. - # go = true; # <- disabled but you could enable it with override or module on install - js = true; - - # this does not have an associated category of plugins, - # but lua can still check for it - lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - appName = "jsvim"; - logo = langModules.javascript.logo; - js-debug-path = "${pkgs.vscode-js-debug.outPath}/lib/node_modules/js-debug/dist/src/dapDebugServer.js"; - }; - extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; - }; - - jvim = { pkgs, ... }: - let - lang = import ./nix/languages/java.nix { inherit pkgs; }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions - settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `jvim`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - }; - # enable the categories you want from categoryDefinitions - categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the java category, - # and ALSO debug.java and debug.default due to our extraCats in categoryDefinitions. - java = true; - - # this does not have an associated category of plugins, - # but lua can still check for it - lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - appName = lang.appName; - logo = lang.logo; - ls-path = lang.ls-path; - }; - extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; - }; - - sharpvim = { pkgs, ... }: - let - lang = import ./nix/languages/csharp.nix { inherit pkgs; }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions - settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `sharpvim`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - }; - # enable the categories you want from categoryDefinitions - categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the csharp category, - # and ALSO debug.csharp and debug.default due to our extraCats in categoryDefinitions. - csharp = true; - - # this does not have an associated category of plugins, - # but lua can still check for it - lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - appName = lang.appName; - logo = lang.logo; - ls-path = lang.ls-path; - }; - extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; - }; - - zvim = { pkgs, ... }: - let - lang = import ./nix/languages/zig.nix { inherit pkgs; }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions - settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `zvim`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; - }; - # enable the categories you want from categoryDefinitions - categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the zig category, - # and ALSO debug.zig and debug.default due to our extraCats in categoryDefinitions. - zig = true; - - # this does not have an associated category of plugins, - # but lua can still check for it - lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - appName = lang.appName; - logo = lang.logo; - ls-path = lang.ls-path; - }; - extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; - }; - - rustvim = { pkgs, ... }: - let - lang = import ./nix/languages/rust.nix { inherit pkgs; }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions - settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `rustvim`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; + aliases = [ "vimcat" ]; + wrapRc = true; + configDirName = "nixCats-nvim"; }; - # enable the categories you want from categoryDefinitions categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the rust category, - # and ALSO debug.rust and debug.default due to our extraCats in categoryDefinitions. - rust = true; - - # this does not have an associated category of plugins, - # but lua can still check for it + markdown = true; + general = true; + lint = true; + format = true; + neonixdev = true; + ai = true; + test = { subtest1 = true; }; lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; + themer = true; colorscheme = "onedark"; - appName = lang.appName; - logo = lang.logo; - ls-path = lang.ls-path; - codelldb-path = lang.codelldb-path; }; extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; + nixdExtras = { nixpkgs = nixpkgs; }; + languageConfig = moxLib.mkLanguageConfig pkgs; }; }; - rvim = { pkgs, ... }: - let - lang = import ./nix/languages/r.nix { inherit pkgs; }; - in { - # these also recieve our pkgs variable - # see :help nixCats.flake.outputs.packageDefinitions + regularCats = { pkgs, ... }: { settings = { - # The name of the package, and the default launch name, - # and the name of the .desktop file, is `rvim`, - # or, whatever you named the package definition in the packageDefinitions set. - # WARNING: MAKE SURE THESE DONT CONFLICT WITH OTHER INSTALLED PACKAGES ON YOUR PATH - # That would result in a failed build, as nixos and home manager modules validate for collisions on your path - aliases = [ ]; - - # explained below in the `regularCats` package's definition - # OR see :help nixCats.flake.outputs.settings for all of the settings available - wrapRc = true; - configDirName = "mox-nvim"; - # neovim-unwrapped = inputs.neovim-nightly-overlay.packages.${pkgs.system}.neovim; + wrapRc = false; + configDirName = "nixCats-nvim"; + aliases = [ "testCat" ]; }; - # enable the categories you want from categoryDefinitions categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; - - # enabling this category will enable the r category, - # and ALSO debug.r and debug.default due to our extraCats in categoryDefinitions. - r = true; - - # this does not have an associated category of plugins, - # but lua can still check for it + markdown = true; + general = true; + neonixdev = true; + lint = true; + format = true; + test = true; lspDebugMode = false; - # you could also pass something else: - # see :help nixCats - themer = true; - colorscheme = "onedark"; - appName = lang.appName; - logo = lang.logo; + themer = true; + colorscheme = "catppuccin"; }; extra = { - # to keep the categories table from being filled with non category things that you want to pass - # there is also an extra table you can use to pass extra stuff. - # but you can pass all the same stuff in any of these sets and access it in lua - nixdExtras = { - nixpkgs = nixpkgs; - }; + nixdExtras = { nixpkgs = nixpkgs; }; + languageConfig = moxLib.mkLanguageConfig pkgs; }; }; }; - defaultPackageName = "nvim"; + defaultPackageName = "jsvim"; - # defaultPackageName is also passed to utils.mkNixosModules and utils.mkHomeModules - # and it controls the name of the top level option set. - # If you made a package named `nixCats` your default package as we did here, - # the modules generated would be set at: - # config.nixCats = { - # enable = true; - # packageNames = [ "nixCats" ]; # <- the packages you want installed - # - # } - # In addition, every package exports its own module via passthru, and is overrideable. - # so you can yourpackage.homeModule and then the namespace would be that packages name. in - # you shouldnt need to change much past here, but you can if you wish. - # but you should at least eventually try to figure out whats going on here! - # see :help nixCats.flake.outputs.exports forEachSystem (system: let - # and this will be our builder! it takes a name from our packageDefinitions as an argument, and builds an nvim. nixCatsBuilder = utils.baseBuilder luaPath { - # we pass in the things to make a pkgs variable to build nvim with later inherit nixpkgs system dependencyOverlays extra_pkg_config; - # and also our categoryDefinitions and packageDefinitions } categoryDefinitions packageDefinitions; - # call it with our defaultPackageName defaultPackage = nixCatsBuilder defaultPackageName; - - # this pkgs variable is just for using utils such as pkgs.mkShell - # within this outputs set. pkgs = import nixpkgs { inherit system; }; - # The one used to build neovim is resolved inside the builder - # and is passed to our categoryDefinitions and packageDefinitions in { - # these outputs will be wrapped with ${system} by utils.eachSystem - - # this will generate a set of all the packages - # in the packageDefinitions defined above - # from the package we give it. - # and additionally output the original as default. packages = utils.mkAllWithDefault defaultPackage; - # choose your package for devShell - # and add whatever else you want in it. devShells = { default = pkgs.mkShell { name = defaultPackageName; packages = [ defaultPackage ]; inputsFrom = [ ]; - shellHook = '' - ''; + shellHook = '' ''; }; }; - }) // (let - # we also export a nixos module to allow reconfiguration from configuration.nix nixosModule = utils.mkNixosModules { inherit defaultPackageName dependencyOverlays luaPath categoryDefinitions packageDefinitions extra_pkg_config nixpkgs; }; - # and the same for home manager homeModule = utils.mkHomeModules { inherit defaultPackageName dependencyOverlays luaPath categoryDefinitions packageDefinitions extra_pkg_config nixpkgs; }; in { - - # these outputs will be NOT wrapped with ${system} - - # this will make an overlay out of each of the packageDefinitions defined above - # and set the default overlay to the one named here. overlays = utils.makeOverlays luaPath { inherit nixpkgs dependencyOverlays extra_pkg_config; } categoryDefinitions packageDefinitions defaultPackageName; nixosModules.default = nixosModule; - homeModules.default = homeModule; + homeModules.default = homeModule; inherit utils nixosModule homeModule; inherit (utils) templates; }); - } diff --git a/lua/jsvim/health.lua b/lua/jsvim/health.lua new file mode 100644 index 0000000..2e6b30d --- /dev/null +++ b/lua/jsvim/health.lua @@ -0,0 +1,86 @@ +---:checkhealth jsvim +--- +---Reports whether the JS/TS toolchain (node, eslint_d, prettierd, +---vscode-js-debug, ts_ls, etc.) is available, which LSP clients are +---attached to the current buffer, and whether they advertise inlay +---hint support. + +local M = {} + +local h = vim.health or require('health') +local ok = h.ok or h.report_ok +local warn = h.warn or h.report_warn +local error_ = h.error or h.report_error +local info = h.info or h.report_info +local start = h.start or h.report_start + +local function check_exe(name, friendly) + if vim.fn.executable(name) == 1 then + ok(('%s found at %s'):format(friendly or name, vim.fn.exepath(name))) + return true + else + warn(('%s not found on PATH'):format(friendly or name)) + return false + end +end + +function M.check() + start('jsvim: runtime') + check_exe('node', 'node') + check_exe('npm', 'npm') + check_exe('npx', 'npx') + if vim.fn.executable('pnpm') == 1 then check_exe('pnpm', 'pnpm') end + if vim.fn.executable('yarn') == 1 then check_exe('yarn', 'yarn') end + if vim.fn.executable('bun') == 1 then check_exe('bun', 'bun') end + check_exe('tsc', 'tsc (TypeScript compiler)') + + start('jsvim: language servers') + check_exe('typescript-language-server', 'ts_ls (TypeScript LSP)') + check_exe('vscode-eslint-language-server', 'eslint LSP') + check_exe('vscode-json-language-server', 'json LSP') + check_exe('vscode-css-language-server', 'css LSP') + check_exe('vscode-html-language-server', 'html LSP') + check_exe('tailwindcss-language-server', 'tailwindcss LSP') + check_exe('emmet-language-server', 'emmet LSP') + check_exe('yaml-language-server', 'yaml LSP') + + start('jsvim: format / lint') + check_exe('prettierd', 'prettierd') + check_exe('prettier', 'prettier') + check_exe('eslint_d', 'eslint_d') + + start('jsvim: debug adapter') + local js_debug = nixCats and nixCats('js-debug-path') + if js_debug and vim.fn.filereadable(js_debug) == 1 then + ok(('vscode-js-debug at %s'):format(js_debug)) + elseif js_debug then + error_(('js-debug-path set but not readable: %s'):format(js_debug)) + else + warn('nixCats("js-debug-path") not set — DAP launch configs will not work') + end + + start('jsvim: attached LSP clients (current buffer)') + local clients = vim.lsp.get_clients({ bufnr = 0 }) + if #clients == 0 then + info('No LSP clients attached to this buffer') + else + for _, c in ipairs(clients) do + local caps = c.server_capabilities or {} + local extras = {} + if caps.inlayHintProvider then table.insert(extras, 'inlayHints') end + if caps.codeActionProvider then table.insert(extras, 'codeAction') end + if caps.documentFormattingProvider then table.insert(extras, 'format') end + if caps.renameProvider then table.insert(extras, 'rename') end + ok(('%s [%s]'):format(c.name, table.concat(extras, ', '))) + end + end + + start('jsvim: nixCats categories') + local cats = { 'js', 'general', 'lint', 'format', 'debug', 'ai', 'general.cmp', 'general.treesitter', 'general.telescope' } + for _, cat in ipairs(cats) do + if nixCats(cat) then ok(('category %s = true'):format(cat)) + else info(('category %s = false'):format(cat)) end + end +end + +return M diff --git a/lua/myLuaConf/LSPs/caps-on_attach.lua b/lua/myLuaConf/LSPs/caps-on_attach.lua index d25dd58..754e859 100644 --- a/lua/myLuaConf/LSPs/caps-on_attach.lua +++ b/lua/myLuaConf/LSPs/caps-on_attach.lua @@ -1,121 +1,100 @@ local M = {} --- Custom hover function with improved window sizing and scrolling +---Custom hover floating window with sane sizing and a `q` / `` close. local function custom_hover() - local client = vim.lsp.get_active_clients({ bufnr = 0 })[1] + local client = vim.lsp.get_clients({ bufnr = 0 })[1] if not client then return end local params = vim.lsp.util.make_position_params(0, client.offset_encoding) - return vim.lsp.buf_request( - 0, - 'textDocument/hover', - params, - function(err, result, ctx, _) - if err or not result then return end - local markdown_lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents) - markdown_lines = vim.lsp.util.trim_empty_lines(markdown_lines) - if vim.tbl_isempty(markdown_lines) then return end - - -- Create a buffer for the hover content - local hover_bufnr = vim.api.nvim_create_buf(false, true) - vim.api.nvim_buf_set_lines(hover_bufnr, 0, -1, false, markdown_lines) - vim.api.nvim_buf_set_option(hover_bufnr, 'filetype', 'markdown') - vim.api.nvim_buf_set_option(hover_bufnr, 'modifiable', false) - vim.api.nvim_buf_set_option(hover_bufnr, 'buflisted', false) - - -- Calculate window dimensions - local width = math.min(100, vim.o.columns - 4) - local height = math.min(30, math.max(5, #markdown_lines + 2)) - - -- Create floating window - local win_id = vim.api.nvim_open_win(hover_bufnr, true, { - relative = 'cursor', - width = width, - height = height, - row = 1, - col = 1, - style = 'minimal', - border = 'rounded', - }) - - vim.api.nvim_win_set_option(win_id, 'wrap', true) - vim.api.nvim_win_set_option(win_id, 'scrollbind', false) - - -- Close function that properly cleans up the window and buffer - local function close_hover() - if vim.api.nvim_win_is_valid(win_id) then - vim.api.nvim_win_close(win_id, true) - end - if vim.api.nvim_buf_is_valid(hover_bufnr) then - vim.api.nvim_buf_delete(hover_bufnr, { force = true }) - end + vim.lsp.buf_request(0, 'textDocument/hover', params, function(err, result, _ctx, _config) + if err or not result then return end + local lines = vim.lsp.util.convert_input_to_markdown_lines(result.contents) + lines = vim.lsp.util.trim_empty_lines(lines) + if vim.tbl_isempty(lines) then return end + + local hover_bufnr = vim.api.nvim_create_buf(false, true) + vim.api.nvim_buf_set_lines(hover_bufnr, 0, -1, false, lines) + vim.bo[hover_bufnr].filetype = 'markdown' + vim.bo[hover_bufnr].modifiable = false + vim.bo[hover_bufnr].buflisted = false + + local width = math.min(100, vim.o.columns - 4) + local height = math.min(30, math.max(5, #lines + 2)) + + local win_id = vim.api.nvim_open_win(hover_bufnr, true, { + relative = 'cursor', + width = width, + height = height, + row = 1, + col = 1, + style = 'minimal', + border = 'rounded', + }) + + vim.wo[win_id].wrap = true + vim.wo[win_id].scrollbind = false + + local function close_hover() + if vim.api.nvim_win_is_valid(win_id) then + vim.api.nvim_win_close(win_id, true) + end + if vim.api.nvim_buf_is_valid(hover_bufnr) then + vim.api.nvim_buf_delete(hover_bufnr, { force = true }) end - - -- Add keymaps for scrolling and closing - local opts = { buffer = hover_bufnr, noremap = true, silent = true } - vim.keymap.set('n', 'q', close_hover, opts) - vim.keymap.set('n', '', close_hover, opts) - vim.keymap.set('n', '', '', opts) -- Page down - vim.keymap.set('n', '', '', opts) -- Page up end - ) -end -function M.on_attach(_, bufnr) - -- we create a function that lets us more easily define mappings specific - -- for LSP related items. It sets the mode, buffer and description for us each time. + local opts = { buffer = hover_bufnr, noremap = true, silent = true } + vim.keymap.set('n', 'q', close_hover, opts) + vim.keymap.set('n', '', close_hover, opts) + vim.keymap.set('n', '', '', opts) + vim.keymap.set('n', '', '', opts) + end) +end +function M.on_attach(client, bufnr) local nmap = function(keys, func, desc) - if desc then - desc = 'LSP: ' .. desc - end - + if desc then desc = 'LSP: ' .. desc end vim.keymap.set('n', keys, func, { buffer = bufnr, desc = desc }) end - nmap('rn', vim.lsp.buf.rename, '[R]e[n]ame') + nmap('rn', vim.lsp.buf.rename, '[R]e[n]ame') nmap('ca', vim.lsp.buf.code_action, '[C]ode [A]ction') - nmap('gd', vim.lsp.buf.definition, '[G]oto [D]efinition') + nmap('gd', vim.lsp.buf.definition, '[G]oto [D]efinition') - -- NOTE: why are these functions that call the telescope builtin? - -- because otherwise they would load telescope eagerly when this is defined. - -- due to us using the on_require handler to make sure it is available. if nixCats('general.telescope') then - nmap('gr', function() require('telescope.builtin').lsp_references() end, '[G]oto [R]eferences') - nmap('gI', function() require('telescope.builtin').lsp_implementations() end, '[G]oto [I]mplementation') - nmap('ds', function() require('telescope.builtin').lsp_document_symbols() end, '[D]ocument [S]ymbols') - nmap('ws', function() require('telescope.builtin').lsp_dynamic_workspace_symbols() end, '[W]orkspace [S]ymbols') - end -- Uses Telescope for symbol navigation; can be replaced with vim.lsp.buf equivalents if preferred + nmap('gr', function() require('telescope.builtin').lsp_references() end, '[G]oto [R]eferences') + nmap('gI', function() require('telescope.builtin').lsp_implementations() end, '[G]oto [I]mplementation') + nmap('ds', function() require('telescope.builtin').lsp_document_symbols() end, '[D]ocument [S]ymbols') + nmap('ws', function() require('telescope.builtin').lsp_dynamic_workspace_symbols() end, '[W]orkspace [S]ymbols') + end nmap('D', vim.lsp.buf.type_definition, 'Type [D]efinition') + nmap('K', custom_hover, 'Hover Documentation') + nmap('', vim.lsp.buf.signature_help, 'Signature Documentation') - -- See `:help K` for why this keymap - -- Using custom hover with improved window sizing - nmap('K', custom_hover, 'Hover Documentation') - nmap('', vim.lsp.buf.signature_help, 'Signature Documentation') - - -- Jump to type definition (shows full type if it's in source) - nmap('D', vim.lsp.buf.type_definition, 'Jump to Type Definition') - - -- Lesser used LSP functionality - nmap('gD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') - nmap('wa', vim.lsp.buf.add_workspace_folder, '[W]orkspace [A]dd Folder') - nmap('wr', vim.lsp.buf.remove_workspace_folder, '[W]orkspace [R]emove Folder') - nmap('wl', function() - print(vim.inspect(vim.lsp.buf.list_workspace_folders())) - end, '[W]orkspace [L]ist Folders') + nmap('gD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') + nmap('wa', vim.lsp.buf.add_workspace_folder, '[W]orkspace [A]dd Folder') + nmap('wr', vim.lsp.buf.remove_workspace_folder, '[W]orkspace [R]emove Folder') + nmap('wl', function() print(vim.inspect(vim.lsp.buf.list_workspace_folders())) end, + '[W]orkspace [L]ist Folders') - -- Create a command `:Format` local to the LSP buffer + ---Per-buffer :Format command. vim.api.nvim_buf_create_user_command(bufnr, 'Format', function(_) vim.lsp.buf.format() end, { desc = 'Format current buffer with LSP' }) + ---Inlay hints: enable per-buffer if the client supports them. + if client and client.server_capabilities and client.server_capabilities.inlayHintProvider then + pcall(vim.lsp.inlay_hint.enable, true, { bufnr = bufnr }) + nmap('th', function() + local enabled = vim.lsp.inlay_hint.is_enabled({ bufnr = bufnr }) + vim.lsp.inlay_hint.enable(not enabled, { bufnr = bufnr }) + end, '[T]oggle inlay [H]ints') + end end -function M.get_capabilities(server_name) - -- nvim-cmp supports additional completion capabilities, so broadcast that to servers - -- if you make a package without it, make sure to check if it exists with nixCats! +function M.get_capabilities(_server_name) local capabilities = vim.lsp.protocol.make_client_capabilities() if nixCats('general.cmp') then capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities()) @@ -123,4 +102,5 @@ function M.get_capabilities(server_name) capabilities.textDocument.completion.completionItem.snippetSupport = true return capabilities end + return M diff --git a/lua/myLuaConf/LSPs/import-utils.lua b/lua/myLuaConf/LSPs/import-utils.lua index afa5088..808ee7a 100644 --- a/lua/myLuaConf/LSPs/import-utils.lua +++ b/lua/myLuaConf/LSPs/import-utils.lua @@ -1,125 +1,93 @@ +---Import-management helpers, mostly for ts_ls / gopls / jdtls. +--- +---For ts_ls we prefer the typed code-action kinds shipped by the server +---(`source.organizeImports.ts`, `source.removeUnused.ts`, +---`source.addMissingImports.ts`) and only fall back to the raw command +---name as a safety net. local M = {} --- Function to organize/fix imports -function M.fix_imports() - local clients = vim.lsp.get_clients({ bufnr = 0 }) - - for _, client in pairs(clients) do - if client.name == "ts_ls" or client.name == "tsserver" then - -- TypeScript/JavaScript - local params = { - command = "_typescript.organizeImports", - arguments = { vim.api.nvim_buf_get_name(0) }, - } - -- Use the new client:exec_cmd method if available, fallback to execute_command - if client.exec_cmd then - client:exec_cmd(params) - else - vim.lsp.buf.execute_command(params) +---Apply the first code action whose kind matches one of `kinds`. +local function apply_code_action(kinds) + vim.lsp.buf.code_action({ + apply = true, + filter = function(action) + if not action.kind then return false end + for _, k in ipairs(kinds) do + if action.kind == k or action.kind:match('^' .. vim.pesc(k)) then + return true + end end - return - elseif client.name == "gopls" then - -- Go - vim.lsp.buf.code_action({ - filter = function(action) - return action.kind and action.kind:match("source.organizeImports") - end, - apply = true, - }) - return - elseif client.name == "jdtls" then - -- Java - vim.lsp.buf.code_action({ - filter = function(action) - return action.kind and action.kind:match("source.organizeImports") - end, - apply = true, - }) - return + return false + end, + }) +end + +---Return the first attached LSP client whose name matches one of `names`. +local function find_client(names) + for _, client in ipairs(vim.lsp.get_clients({ bufnr = 0 })) do + for _, n in ipairs(names) do + if client.name == n then return client end end end + return nil +end - -- Fallback to generic code action approach - local params = vim.lsp.util.make_range_params() - params.context = { - only = { "source.organizeImports" }, - diagnostics = {}, - } - - local result = vim.lsp.buf_request_sync(0, "textDocument/codeAction", params, 1000) - if not result or vim.tbl_isempty(result) then - print("No organize imports action available") +function M.organize_imports() + if find_client({ 'ts_ls', 'tsserver', 'typescript-tools' }) then + apply_code_action({ 'source.organizeImports.ts', 'source.organizeImports' }) return end - - for _, res in pairs(result) do - if res.result then - for _, action in pairs(res.result) do - if action.edit then - vim.lsp.util.apply_workspace_edit(action.edit, "utf-8") - elseif action.command then - -- Use the newer client method if available - local active_clients = vim.lsp.get_clients({ bufnr = 0 }) - if active_clients[1] and active_clients[1].exec_cmd then - active_clients[1]:exec_cmd(action.command) - else - vim.lsp.buf.execute_command(action.command) - end - end - end - end + if find_client({ 'gopls' }) then + apply_code_action({ 'source.organizeImports' }) + return + end + if find_client({ 'jdtls' }) then + apply_code_action({ 'source.organizeImports' }) + return end + apply_code_action({ 'source.organizeImports' }) end --- Function to add missing imports (where supported) function M.add_missing_imports() - vim.lsp.buf.code_action({ - filter = function(action) - return action.kind and ( - action.kind:match("source.addMissingImports") or - action.kind:match("quickfix") - ) - end, - apply = true, - }) + if find_client({ 'ts_ls', 'tsserver', 'typescript-tools' }) then + apply_code_action({ 'source.addMissingImports.ts', 'source.addMissingImports', 'quickfix' }) + return + end + apply_code_action({ 'source.addMissingImports', 'quickfix' }) end --- Function to remove unused imports function M.remove_unused_imports() - vim.lsp.buf.code_action({ - filter = function(action) - return action.kind and action.kind:match("source.removeUnused") - end, - apply = true, - }) + if find_client({ 'ts_ls', 'tsserver', 'typescript-tools' }) then + apply_code_action({ 'source.removeUnused.ts', 'source.removeUnused' }) + return + end + apply_code_action({ 'source.removeUnused' }) end --- Function to fix all import issues +---Run add-missing -> remove-unused -> organize, with small delays so each +---batch of edits settles before the next request goes out. function M.fix_all_imports() M.add_missing_imports() vim.defer_fn(function() M.remove_unused_imports() - vim.defer_fn(function() - M.fix_imports() - end, 100) + vim.defer_fn(M.organize_imports, 100) end, 100) end function M.setup_keybindings(bufnr) local opts = { buffer = bufnr, silent = true } - vim.keymap.set('n', 'oi', M.fix_imports, - vim.tbl_extend('force', opts, { desc = 'Organize/Fix Imports' })) - + vim.keymap.set('n', 'oi', M.organize_imports, + vim.tbl_extend('force', opts, { desc = 'Organize / Fix Imports' })) vim.keymap.set('n', 'ia', M.add_missing_imports, vim.tbl_extend('force', opts, { desc = 'Add Missing Imports' })) - vim.keymap.set('n', 'ir', M.remove_unused_imports, vim.tbl_extend('force', opts, { desc = 'Remove Unused Imports' })) - vim.keymap.set('n', 'if', M.fix_all_imports, vim.tbl_extend('force', opts, { desc = 'Fix All Import Issues' })) end -return M +---Backwards-compatible alias; older config called this `fix_imports`. +M.fix_imports = M.organize_imports +return M diff --git a/lua/myLuaConf/LSPs/init.lua b/lua/myLuaConf/LSPs/init.lua index cce101e..ef749d0 100644 --- a/lua/myLuaConf/LSPs/init.lua +++ b/lua/myLuaConf/LSPs/init.lua @@ -1,135 +1,214 @@ +---LSP setup driven by the nixCats categories that this package was built +---with. Servers are defined in a single `servers` table, then either: +--- - on Nix: enabled directly via vim.lsp.config + vim.lsp.enable +--- - off Nix: installed by mason and enabled via mason-lspconfig v2 API +--- +---Inlay hints, diagnostic UX and on_attach keybindings live in +---`caps-on_attach.lua`. + local servers = {} + +---Common JS/TS root markers — covers monorepos (pnpm/yarn/nx/turborepo) +---and standalone projects. +local js_root_markers = { + 'package.json', 'tsconfig.json', 'jsconfig.json', + 'pnpm-workspace.yaml', 'yarn.lock', 'pnpm-lock.yaml', 'bun.lockb', + 'nx.json', 'turbo.json', + '.git', +} + +---Try to load SchemaStore.nvim; it's only present when the `js` category +---is enabled. The require is wrapped because the plugin may not be packadd'd +---at the time this file is loaded. +local function schemastore_json_schemas() + local ok, ss = pcall(require, 'schemastore') + if not ok then return nil end + return { schemas = ss.json.schemas(), validate = { enable = true } } +end + +local function schemastore_yaml_schemas() + local ok, ss = pcall(require, 'schemastore') + if not ok then return nil end + return ss.yaml.schemas() +end + +----------------------------------------------------------------- neonixdev -- + if nixCats('neonixdev') then servers.lua_ls = { settings = { Lua = { - formatters = { - ignoreComments = true, - }, + formatters = { ignoreComments = true }, signatureHelp = { enabled = true }, - diagnostics = { + diagnostics = { globals = { 'nixCats' }, disable = { 'missing-fields' }, }, + telemetry = { enabled = false }, }, - telemetry = { enabled = false }, - }, filetypes = { 'lua' }, } + if require('nixCatsUtils').isNixCats then servers.nixd = { settings = { nixd = { nixpkgs = { - -- nixd requires some configuration in flake based configs. - -- luckily, the nixCats plugin is here to pass whatever we need! - -- we passed this in via the `extra` table in our packageDefinitions - -- for additional configuration options, refer to: - -- https://github.com/nix-community/nixd/blob/main/nixd/docs/configuration.md - expr = [[import (builtins.getFlake "]] .. nixCats.extra("nixdExtras.nixpkgs") .. [[") { } ]], + expr = [[import (builtins.getFlake "]] .. nixCats.extra('nixdExtras.nixpkgs') .. [[") { } ]], }, - formatting = { - command = { "nixfmt" } - }, - diagnostic = { - suppress = { - "sema-escaping-with" - } - } - } - } + formatting = { command = { 'nixfmt' } }, + diagnostic = { suppress = { 'sema-escaping-with' } }, + }, + }, } - -- If you integrated with your system flake, - -- you should pass inputs.self as nixdExtras.flake-path - -- that way it will ALWAYS work, regardless - -- of where your config actually was. - -- otherwise flake-path could be an absolute path to your system flake, or nil or false - if nixCats.extra("nixdExtras.flake-path") then - local flakePath = nixCats.extra("nixdExtras.flake-path") - if nixCats.extra("nixdExtras.systemCFGname") then - -- (builtins.getFlake "").nixosConfigurations."".options + if nixCats.extra('nixdExtras.flake-path') then + local flakePath = nixCats.extra('nixdExtras.flake-path') + if nixCats.extra('nixdExtras.systemCFGname') then + servers.nixd.settings.nixd.options = servers.nixd.settings.nixd.options or {} servers.nixd.settings.nixd.options.nixos = { - expr = [[(builtins.getFlake "]] .. flakePath .. [[").nixosConfigurations."]] .. - nixCats.extra("nixdExtras.systemCFGname") .. [[".options]] + expr = [[(builtins.getFlake "]] .. flakePath .. [[").nixosConfigurations."]] .. + nixCats.extra('nixdExtras.systemCFGname') .. [[".options]], } end - if nixCats.extra("nixdExtras.homeCFGname") then - -- (builtins.getFlake "").homeConfigurations."".options - servers.nixd.settings.nixd.options["home-manager"] = { - expr = [[(builtins.getFlake "]] .. flakePath .. [[").homeConfigurations."]] - .. nixCats.extra("nixdExtras.homeCFGname") .. [[".options]] + if nixCats.extra('nixdExtras.homeCFGname') then + servers.nixd.settings.nixd.options = servers.nixd.settings.nixd.options or {} + servers.nixd.settings.nixd.options['home-manager'] = { + expr = [[(builtins.getFlake "]] .. flakePath .. [[").homeConfigurations."]] .. + nixCats.extra('nixdExtras.homeCFGname') .. [[".options]], } end end else - servers.rnix = {} servers.nil_ls = {} end - end +----------------------------------------------------------------- go --------- + if nixCats('go') then servers.gopls = {} end +----------------------------------------------------------------- js / ts ---- + if nixCats('js') then + ---Inlay hints config, valid for typescript-language-server. + local ts_inlay = { + includeInlayParameterNameHints = 'all', + includeInlayParameterNameHintsWhenArgumentMatchesName = false, + includeInlayFunctionParameterTypeHints = true, + includeInlayVariableTypeHints = true, + includeInlayPropertyDeclarationTypeHints = true, + includeInlayFunctionLikeReturnTypeHints = true, + includeInlayEnumMemberValueHints = true, + } + servers.ts_ls = { + root_markers = js_root_markers, settings = { typescript = { - displayStringForProperties = true, - preferDisplayStringForProperties = true, + inlayHints = ts_inlay, + implementationsCodeLens = { enabled = true }, + referencesCodeLens = { enabled = true, showOnAllFunctions = false }, + preferences = { includeCompletionsForModuleExports = true }, }, javascript = { - displayStringForProperties = true, - preferDisplayStringForProperties = true, + inlayHints = ts_inlay, + implementationsCodeLens = { enabled = true }, + referencesCodeLens = { enabled = true, showOnAllFunctions = false }, + }, + }, + } + + ---ESLint LSP from vscode-langservers-extracted. + servers.eslint = { + root_markers = vim.list_extend({ + 'eslint.config.js', 'eslint.config.mjs', 'eslint.config.cjs', 'eslint.config.ts', + '.eslintrc', '.eslintrc.js', '.eslintrc.cjs', '.eslintrc.json', '.eslintrc.yaml', '.eslintrc.yml', + }, js_root_markers), + settings = { + workingDirectories = { mode = 'auto' }, + codeActionOnSave = { enable = true, mode = 'all' }, + experimental = { useFlatConfig = true }, + problems = { shortenToSingleLine = false }, + run = 'onType', + }, + } + + ---JSON / YAML / CSS / HTML / Tailwind / Emmet — all via the + ---vscode-langservers-extracted family + the standalone ones. + servers.jsonls = { + settings = { json = schemastore_json_schemas() or { validate = { enable = true } } }, + init_options = { provideFormatter = true }, + } + servers.yamlls = { + settings = { + yaml = { + schemaStore = { enable = false, url = '' }, + schemas = schemastore_yaml_schemas() or {}, }, }, } + servers.cssls = { root_markers = js_root_markers } + servers.html = { root_markers = js_root_markers } + servers.tailwindcss = { root_markers = vim.list_extend({ + 'tailwind.config.js', 'tailwind.config.cjs', 'tailwind.config.mjs', 'tailwind.config.ts', + }, js_root_markers) } + servers.emmet_language_server = { + filetypes = { + 'html', 'css', 'scss', 'less', 'sass', + 'javascriptreact', 'typescriptreact', 'svelte', 'vue', + }, + } end +----------------------------------------------------------------- java ------- + if nixCats('java') then servers.jdtls = {} end +----------------------------------------------------------------- csharp ----- + if nixCats('csharp') then servers.omnisharp = {} end +----------------------------------------------------------------- zig -------- + if nixCats('zig') then servers.zls = { settings = { zls = { enable_semantic_tokens = true, - enable_inlay_hints = true, - enable_snippets = true, - warn_style = true, - enable_autofix = false, + enable_inlay_hints = true, + enable_snippets = true, + warn_style = true, + enable_autofix = false, }, }, filetypes = { 'zig' }, } end +----------------------------------------------------------------- rust ------- + if nixCats('rust') then servers.rust_analyzer = { settings = { - ["rust-analyzer"] = { - check = { - command = "clippy", - }, - cargo = { - allFeatures = true, - loadOutDirsFromCheck = true, - }, - procMacro = { - enable = true, - }, - diagnostics = { - enable = true, - experimental = { - enable = true, - }, + ['rust-analyzer'] = { + check = { command = 'clippy' }, + cargo = { allFeatures = true, loadOutDirsFromCheck = true }, + procMacro = { enable = true }, + diagnostics = { enable = true, experimental = { enable = true } }, + inlayHints = { + bindingModeHints = { enable = true }, + chainingHints = { enable = true }, + closingBraceHints = { enable = true, minLines = 25 }, + closureReturnTypeHints = { enable = 'always' }, + parameterHints = { enable = true }, + typeHints = { enable = true, hideClosureInitialization = false, hideNamedConstructor = false }, }, }, }, @@ -137,101 +216,93 @@ if nixCats('rust') then } end +----------------------------------------------------------------- r --------- + if nixCats('r') then servers.r_language_server = { settings = { r = { - lsp = { - debug = false, - diagnostics = true, - rich_documentation = true, - }, + lsp = { debug = false, diagnostics = true, rich_documentation = true }, }, }, filetypes = { 'r', 'rmd', 'qmd' }, } end --- This is this flake's version of what kickstarter has set up for mason handlers. --- This is a convenience function that calls lspconfig on the lsps we downloaded via nix --- This will not download your lsp. Nix does that. - --- Add any additional override configuration in the following tables. They will be passed to --- the `settings` field of the server config. You must look up that documentation yourself. --- All of them are listed in https://github.com/neovim/nvim-lspconfig/blob/master/doc/configs.md --- --- If you want to override the default filetypes that your language server will attach to you can --- define the property 'filetypes' to the map in question. --- You may do the same thing with cmd - --- servers.clangd = {}, --- servers.gopls = {}, --- servers.pyright = {}, --- servers.rust_analyzer = {}, --- servers.tsserver = {}, --- servers.html = { filetypes = { 'html', 'twig', 'hbs'} }, - +----------------------------------------------------------------- attach ---- if not require('nixCatsUtils').isNixCats and nixCats('lspDebugMode') then - vim.lsp.set_log_level("debug") + vim.lsp.set_log_level('debug') end --- If you were to comment out this autocommand --- and instead pass the on attach function directly to --- nvim-lspconfig, it would do the same thing. --- come to think of it, it might be better because then lspconfig doesnt have to be called before lsp attach? --- but you would still end up triggering on a FileType event anyway, so, it makes little difference. + vim.api.nvim_create_autocmd('LspAttach', { - group = vim.api.nvim_create_augroup('nixCats-lsp-attach', { clear = true }), + group = vim.api.nvim_create_augroup('nixCats-lsp-attach', { clear = true }), callback = function(event) - require('myLuaConf.LSPs.caps-on_attach').on_attach(vim.lsp.get_client_by_id(event.data.client_id), event.buf) + local client = vim.lsp.get_client_by_id(event.data.client_id) + require('myLuaConf.LSPs.caps-on_attach').on_attach(client, event.buf) require('myLuaConf.LSPs.import-utils').setup_keybindings(event.buf) - end + end, }) require('lze').load { { - "nvim-lspconfig", - for_cat = "general.always", - event = "FileType", - load = (require('nixCatsUtils').isNixCats and vim.cmd.packadd) or function(name) + 'nvim-lspconfig', + for_cat = 'general.always', + event = 'FileType', + load = (require('nixCatsUtils').isNixCats and vim.cmd.packadd) or function(name) vim.cmd.packadd(name) - vim.cmd.packadd("mason.nvim") - vim.cmd.packadd("mason-lspconfig.nvim") + vim.cmd.packadd('mason.nvim') + vim.cmd.packadd('mason-lspconfig.nvim') end, - after = function(plugin) + after = function(_plugin) + local on_attach_mod = require('myLuaConf.LSPs.caps-on_attach') + + ---Resolve `settings.json.schemas` lazily via SchemaStore.nvim if it + ---wasn't available at definition time (e.g. lze hadn't packadd'd it). + local function rehydrate_schemas() + if servers.jsonls and (not servers.jsonls.settings.json.schemas) then + local s = schemastore_json_schemas() + if s then servers.jsonls.settings.json = s end + end + if servers.yamlls and (not next(servers.yamlls.settings.yaml.schemas or {})) then + local s = schemastore_yaml_schemas() + if s then servers.yamlls.settings.yaml.schemas = s end + end + end + rehydrate_schemas() + if require('nixCatsUtils').isNixCats then - -- Use native Neovim 0.11+ vim.lsp.config API instead of deprecated lspconfig.setup for server_name, cfg in pairs(servers) do + cfg = cfg or {} vim.lsp.config(server_name, { - cmd = (cfg or {}).cmd, - filetypes = (cfg or {}).filetypes, - root_markers = (cfg or {}).root_markers or { '.git' }, - settings = (cfg or {}).settings, - capabilities = require('myLuaConf.LSPs.caps-on_attach').get_capabilities(server_name), - -- on_attach is handled by the LspAttach autocommand above (lines 183-189) + cmd = cfg.cmd, + filetypes = cfg.filetypes, + init_options = cfg.init_options, + root_markers = cfg.root_markers or { '.git' }, + settings = cfg.settings, + capabilities = on_attach_mod.get_capabilities(server_name), }) vim.lsp.enable(server_name) end else + ---mason-lspconfig v2 API: ensure_installed + vim.lsp.enable. require('mason').setup() - local mason_lspconfig = require 'mason-lspconfig' - mason_lspconfig.setup { + require('mason-lspconfig').setup({ ensure_installed = vim.tbl_keys(servers), - } - mason_lspconfig.setup_handlers { - function(server_name) - -- Use native Neovim 0.11+ vim.lsp.config API instead of deprecated lspconfig.setup - vim.lsp.config(server_name, { - filetypes = (servers[server_name] or {}).filetypes, - root_markers = (servers[server_name] or {}).root_markers or { '.git' }, - settings = (servers[server_name] or {}).settings, - capabilities = require('myLuaConf.LSPs.caps-on_attach').get_capabilities(server_name), - -- on_attach is handled by the LspAttach autocommand above (lines 183-189) - }) - vim.lsp.enable(server_name) - end, - } + automatic_enable = false, + }) + for server_name, cfg in pairs(servers) do + cfg = cfg or {} + vim.lsp.config(server_name, { + filetypes = cfg.filetypes, + init_options = cfg.init_options, + root_markers = cfg.root_markers or { '.git' }, + settings = cfg.settings, + capabilities = on_attach_mod.get_capabilities(server_name), + }) + vim.lsp.enable(server_name) + end end end, - } + }, } diff --git a/lua/myLuaConf/debug/init.lua b/lua/myLuaConf/debug/init.lua index aa9783c..f3f1406 100644 --- a/lua/myLuaConf/debug/init.lua +++ b/lua/myLuaConf/debug/init.lua @@ -1,431 +1,389 @@ +---DAP setup. The base nvim-dap config + dap-ui + virtual text live in +---the first spec; per-language configs (js, zig, rust, csharp, java) live +---in their own specs gated by `for_cat`. +--- +---For JavaScript/TypeScript we drive vscode-js-debug directly: a single +---server-mode adapter (`pwa-node` / `pwa-chrome`) that nvim-dap launches +---via `node ${js-debug-path} ${port}`, where `js-debug-path` is provided +---by the JS language nix module. + require('lze').load { { - "nvim-dap", - -- NOTE: I dont want to figure out mason tools installer for this, so I only enabled debug if nix loaded config + 'nvim-dap', for_cat = { cat = 'debug', default = false }, - -- cmd = { "" }, - -- event = "", - -- ft = "", keys = { - { "", desc = "Debug: Start/Continue" }, - { "", desc = "Debug: Step Into" }, - { "", desc = "Debug: Step Over" }, - { "", desc = "Debug: Step Out" }, - { "b", desc = "Debug: Toggle Breakpoint" }, - { "B", desc = "Debug: Set Breakpoint" }, - { "", desc = "Debug: See last session result." }, + { '', desc = 'Debug: Start/Continue' }, + { '', desc = 'Debug: Step Into' }, + { '', desc = 'Debug: Step Over' }, + { '', desc = 'Debug: Step Out' }, + { 'b', desc = 'Debug: Toggle Breakpoint' }, + { 'B', desc = 'Debug: Conditional Breakpoint' }, + { '', desc = 'Debug: Toggle DAP UI' }, }, - -- colorscheme = "", load = (require('nixCatsUtils').isNixCats and function(name) vim.cmd.packadd(name) - vim.cmd.packadd("nvim-dap-ui") - vim.cmd.packadd("nvim-dap-virtual-text") + vim.cmd.packadd('nvim-dap-ui') + vim.cmd.packadd('nvim-dap-virtual-text') end) or function(name) vim.cmd.packadd(name) - vim.cmd.packadd("nvim-dap-ui") - vim.cmd.packadd("nvim-dap-virtual-text") - vim.cmd.packadd("mason-nvim-dap.nvim") + vim.cmd.packadd('nvim-dap-ui') + vim.cmd.packadd('nvim-dap-virtual-text') + vim.cmd.packadd('mason-nvim-dap.nvim') end, - after = function (plugin) - local dap = require 'dap' - local dapui = require 'dapui' + after = function(_plugin) + local dap = require('dap') + local dapui = require('dapui') - -- Basic debugging keymaps, feel free to change to your liking! - vim.keymap.set('n', '', dap.continue, { desc = 'Debug: Start/Continue' }) - vim.keymap.set('n', '', dap.step_into, { desc = 'Debug: Step Into' }) - vim.keymap.set('n', '', dap.step_over, { desc = 'Debug: Step Over' }) - vim.keymap.set('n', '', dap.step_out, { desc = 'Debug: Step Out' }) + vim.keymap.set('n', '', dap.continue, { desc = 'Debug: Start/Continue' }) + vim.keymap.set('n', '', dap.step_into, { desc = 'Debug: Step Into' }) + vim.keymap.set('n', '', dap.step_over, { desc = 'Debug: Step Over' }) + vim.keymap.set('n', '', dap.step_out, { desc = 'Debug: Step Out' }) vim.keymap.set('n', 'b', dap.toggle_breakpoint, { desc = 'Debug: Toggle Breakpoint' }) vim.keymap.set('n', 'B', function() - dap.set_breakpoint(vim.fn.input 'Breakpoint condition: ') + dap.set_breakpoint(vim.fn.input('Breakpoint condition: ')) end, { desc = 'Debug: Set Breakpoint' }) - - -- Toggle to see last session result. Without this, you can't see session output in case of unhandled exception. - vim.keymap.set('n', '', dapui.toggle, { desc = 'Debug: See last session result.' }) + vim.keymap.set('n', '', dapui.toggle, { desc = 'Debug: Toggle DAP UI' }) dap.listeners.after.event_initialized['dapui_config'] = dapui.open dap.listeners.before.event_terminated['dapui_config'] = dapui.close - dap.listeners.before.event_exited['dapui_config'] = dapui.close + dap.listeners.before.event_exited['dapui_config'] = dapui.close - -- Dap UI setup - -- For more information, see |:help nvim-dap-ui| - dapui.setup { - -- Set icons to characters that are more likely to work in every terminal. - -- Feel free to remove or use ones that you like more! :) - -- Don't feel like these are good choices. + dapui.setup({ icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, controls = { icons = { - pause = '⏸', - play = '▶', - step_into = '⏎', - step_over = '⏭', - step_out = '⏮', - step_back = 'b', - run_last = '▶▶', - terminate = '⏹', - disconnect = '⏏', + pause = '⏸', play = '▶', step_into = '⏎', + step_over = '⏭', step_out = '⏮', step_back = 'b', + run_last = '▶▶', terminate = '⏹', disconnect = '⏏', }, }, - } + }) - require("nvim-dap-virtual-text").setup { - enabled = true, -- enable this plugin (the default) - enabled_commands = true, -- create commands DapVirtualTextEnable, DapVirtualTextDisable, DapVirtualTextToggle, (DapVirtualTextForceRefresh for refreshing when debug adapter did not notify its termination) - highlight_changed_variables = true, -- highlight changed values with NvimDapVirtualTextChanged, else always NvimDapVirtualText - highlight_new_as_changed = false, -- highlight new variables in the same way as changed variables (if highlight_changed_variables) - show_stop_reason = true, -- show stop reason when stopped for exceptions - commented = false, -- prefix virtual text with comment string - only_first_definition = true, -- only show virtual text at first definition (if there are multiple) - all_references = false, -- show virtual text on all all references of the variable (not only definitions) - clear_on_continue = false, -- clear virtual text on "continue" (might cause flickering when stepping) - --- A callback that determines how a variable is displayed or whether it should be omitted - --- variable Variable https://microsoft.github.io/debug-adapter-protocol/specification#Types_Variable - --- buf number - --- stackframe dap.StackFrame https://microsoft.github.io/debug-adapter-protocol/specification#Types_StackFrame - --- node userdata tree-sitter node identified as variable definition of reference (see `:h tsnode`) - --- options nvim_dap_virtual_text_options Current options for nvim-dap-virtual-text - --- string|nil A text how the virtual text should be displayed or nil, if this variable shouldn't be displayed - display_callback = function(variable, buf, stackframe, node, options) + require('nvim-dap-virtual-text').setup({ + enabled = true, + enabled_commands = true, + highlight_changed_variables = true, + highlight_new_as_changed = false, + show_stop_reason = true, + commented = false, + only_first_definition = true, + all_references = false, + clear_on_continue = false, + display_callback = function(variable, _buf, _stackframe, _node, options) if options.virt_text_pos == 'inline' then return ' = ' .. variable.value else return variable.name .. ' = ' .. variable.value end end, - -- position of virtual text, see `:h nvim_buf_set_extmark()`, default tries to inline the virtual text. Use 'eol' to set to end of line - virt_text_pos = vim.fn.has 'nvim-0.10' == 1 and 'inline' or 'eol', - - -- experimental features: - all_frames = false, -- show virtual text for all stack frames not only current. Only works for debugpy on my machine. - virt_lines = false, -- show virtual lines instead of virtual text (will flicker!) - virt_text_win_col = nil -- position the virtual text at a fixed window column (starting from the first text column) , - -- e.g. 80 to position at column 80, see `:h nvim_buf_set_extmark()` - } - - -- NOTE: Install lang specific config - -- either in here, or in a separate plugin spec as demonstrated for go below. - + virt_text_pos = vim.fn.has('nvim-0.10') == 1 and 'inline' or 'eol', + all_frames = false, + virt_lines = false, + virt_text_win_col = nil, + }) end, }, + + ----------------------------------------------------------------- go -------- { - "nvim-dap-go", - for_cat = { cat = 'debug.go', default = false }, - on_plugin = { "nvim-dap", }, - after = function(plugin) - require("dap-go").setup() - end, + 'nvim-dap-go', + for_cat = { cat = 'debug.go', default = false }, + on_plugin = { 'nvim-dap' }, + after = function(_plugin) require('dap-go').setup() end, }, + + ----------------------------------------------------------------- js / ts --- { - "nvim-dap-js", - for_cat = { cat = 'debug.js', default = false }, - on_plugin = { "nvim-dap", }, - after = function(plugin) - local dap = require 'dap' - local debug = nixCats("js-debug-path") - -- Use node2 adapter which works directly with Node.js inspector - dap.adapters["node"] = { - type = "executable", - command = "node", - args = { - vim.fn.stdpath("data") .. "/mason/bin/node-debug2-adapter" - } - } + 'nvim-dap', + for_cat = { cat = 'debug.js', default = false }, + after = function(_plugin) + local dap = require('dap') - -- Fallback if mason adapter doesn't exist - direct inspector connection - if vim.fn.executable(vim.fn.stdpath("data") .. "/mason/bin/node-debug2-adapter") == 0 then - dap.adapters["node"] = function(callback, config) - -- For attach requests, connect directly to the inspector port - if config.request == "attach" then - callback({ - type = "server", - host = config.address or "localhost", - port = config.port or 9229 - }) - else - callback(nil, "Only attach mode supported") - end + ---Path to vscode-js-debug's dapDebugServer.js entry, supplied by Nix. + local js_debug = nixCats('js-debug-path') + + ---Single server-mode adapter used by all pwa-* configs. nvim-dap + ---spawns `node ` and connects via TCP. + if js_debug then + local function js_adapter(name) + return { + type = 'server', + host = 'localhost', + port = '${port}', + executable = { + command = 'node', + args = { js_debug, '${port}' }, + }, + } end + dap.adapters['pwa-node'] = js_adapter('pwa-node') + dap.adapters['pwa-chrome'] = js_adapter('pwa-chrome') + dap.adapters['pwa-msedge'] = js_adapter('pwa-msedge') + ---node and chrome aliases for compatibility with various test runners. + dap.adapters['node'] = dap.adapters['pwa-node'] + dap.adapters['chrome'] = dap.adapters['pwa-chrome'] end - -- Keep pwa-node as alias - dap.adapters["pwa-node"] = dap.adapters["node"] - - dap.adapters["pwa-chrome"] = { - type = "server", - host = "localhost", - port = "${port}", - executable = { - command = "node", - args = { debug, "${port}", }, - } - } - - -- Handle mason-nvim-dap setup for non-nix environments without letting it override our configs - if not require('nixCatsUtils').isNixCats then - require("mason-nvim-dap").setup({ - automatic_setup = false, -- Don't auto-setup configurations - handlers = {}, -- No handlers to prevent automatic configuration - }) + ---Read npm scripts out of the nearest package.json for the script picker. + local function pick_npm_script() + local pkg = vim.fs.find('package.json', { upward = true, type = 'file' })[1] + if not pkg then + vim.notify('No package.json found in tree', vim.log.levels.WARN) + return nil + end + local ok, content = pcall(vim.fn.readfile, pkg) + if not ok then return nil end + local data = vim.json.decode(table.concat(content, '\n')) + local scripts = (data and data.scripts) or {} + local names = vim.tbl_keys(scripts) + if #names == 0 then return nil end + table.sort(names) + local choice = vim.fn.inputlist(vim.list_extend({ 'Pick npm script:' }, + vim.tbl_map(function(n) return n .. ' -- ' .. scripts[n] end, names))) + return names[choice] end - -- Clear any existing configurations to avoid conflicts - dap.configurations.typescript = {} - dap.configurations.javascript = {} - dap.configurations.typescriptreact = {} - dap.configurations.javascriptreact = {} - - -- Comprehensive Node.js and browser debugging configurations - for _, language in ipairs({ "typescript", "javascript", "typescriptreact", "javascriptreact" }) do - dap.configurations[language] = { - -- Standard Node.js attach (default port) + local js_filetypes = { 'javascript', 'typescript', 'javascriptreact', 'typescriptreact' } + for _, ft in ipairs(js_filetypes) do + dap.configurations[ft] = { + ----- launch ---------------------------------------------------- + { + name = 'Launch current file (node)', + type = 'pwa-node', + request = 'launch', + program = '${file}', + cwd = '${workspaceFolder}', + sourceMaps = true, + skipFiles = { '/**', 'node_modules/**' }, + console = 'integratedTerminal', + }, { - name = "Attach to Node.js (port 9229)", - type = "pwa-node", - request = "attach", - address = "localhost", - port = 9229, - localRoot = vim.fn.getcwd(), - remoteRoot = vim.fn.getcwd(), + name = 'Launch current file (tsx)', + type = 'pwa-node', + request = 'launch', + runtimeExecutable = 'npx', + runtimeArgs = { 'tsx' }, + program = '${file}', + cwd = '${workspaceFolder}', sourceMaps = true, - skipFiles = { "/**", "node_modules/**" }, + skipFiles = { '/**', 'node_modules/**' }, + console = 'integratedTerminal', }, - - -- Custom port attach { - name = "Attach to Node.js (custom port)", - type = "pwa-node", - request = "attach", - address = "localhost", - port = function() - return vim.fn.input("Debug port: ", "9229") + name = 'Launch via npm script', + type = 'pwa-node', + request = 'launch', + cwd = '${workspaceFolder}', + runtimeExecutable = 'npm', + runtimeArgs = function() + local s = pick_npm_script() + return s and { 'run', s } or { 'run' } end, - localRoot = vim.fn.getcwd(), - remoteRoot = vim.fn.getcwd(), sourceMaps = true, - skipFiles = { "/**", "node_modules/**" }, + skipFiles = { '/**' }, + console = 'integratedTerminal', }, - -- Process picker - attach to any running Node process + ----- jest ------------------------------------------------------ { - name = "Attach to running Node process", - type = "pwa-node", - request = "attach", - processId = function() - return require("dap.utils").pick_process({ filter = "node" }) - end, - localRoot = vim.fn.getcwd(), - remoteRoot = vim.fn.getcwd(), + name = 'Debug Jest (current file)', + type = 'pwa-node', + request = 'launch', + cwd = '${workspaceFolder}', + runtimeExecutable = 'node', + runtimeArgs = { './node_modules/.bin/jest', '--runInBand', '--no-coverage', '${file}' }, sourceMaps = true, - skipFiles = { "/**", "node_modules/**" }, + skipFiles = { '/**', 'node_modules/**' }, + console = 'integratedTerminal', }, - -- Attach to Chrome/Browser + ----- vitest ---------------------------------------------------- { - name = "Attach to Chrome", - type = "pwa-chrome", - request = "attach", - port = 9222, - webRoot = vim.fn.getcwd(), + name = 'Debug Vitest (current file)', + type = 'pwa-node', + request = 'launch', + cwd = '${workspaceFolder}', + runtimeExecutable = 'node', + runtimeArgs = { './node_modules/vitest/vitest.mjs', '--inspect-brk', '--no-coverage', '--no-file-parallelism', 'run', '${file}' }, sourceMaps = true, - skipFiles = { "/**", "node_modules/**" }, + skipFiles = { '/**', 'node_modules/**' }, + console = 'integratedTerminal', }, - -- Launch Chrome with custom URL + ----- attach ---------------------------------------------------- { - name = "Launch Chrome with URL", - type = "pwa-chrome", - request = "launch", - url = function() - return vim.fn.input("URL: ", "http://localhost:3000") - end, - webRoot = vim.fn.getcwd(), + name = 'Attach to Node (port 9229)', + type = 'pwa-node', + request = 'attach', + address = 'localhost', + port = 9229, + cwd = '${workspaceFolder}', + sourceMaps = true, + skipFiles = { '/**', 'node_modules/**' }, + }, + { + name = 'Attach to Node (custom port)', + type = 'pwa-node', + request = 'attach', + address = 'localhost', + port = function() return tonumber(vim.fn.input('Debug port: ', '9229')) end, + cwd = '${workspaceFolder}', sourceMaps = true, - skipFiles = { "/**", "node_modules/**", "**/webpack/**" }, + skipFiles = { '/**', 'node_modules/**' }, + }, + { + name = 'Attach to running Node process', + type = 'pwa-node', + request = 'attach', + processId = function() return require('dap.utils').pick_process({ filter = 'node' }) end, + cwd = '${workspaceFolder}', + sourceMaps = true, + skipFiles = { '/**', 'node_modules/**' }, }, + ----- chrome ---------------------------------------------------- + { + name = 'Attach to Chrome (port 9222)', + type = 'pwa-chrome', + request = 'attach', + port = 9222, + webRoot = '${workspaceFolder}', + sourceMaps = true, + skipFiles = { '/**', 'node_modules/**' }, + }, + { + name = 'Launch Chrome (URL)', + type = 'pwa-chrome', + request = 'launch', + url = function() return vim.fn.input('URL: ', 'http://localhost:3000') end, + webRoot = '${workspaceFolder}', + sourceMaps = true, + skipFiles = { '/**', 'node_modules/**', '**/webpack/**' }, + }, } end end, }, + + ----------------------------------------------------------------- zig ------- { - "nvim-dap", + 'nvim-dap', for_cat = { cat = 'debug.zig', default = false }, - after = function(plugin) - local dap = require 'dap' - - -- LLDB adapter configuration for Zig - dap.adapters.lldb = { - type = 'executable', - command = 'lldb-vscode', - name = 'lldb' - } - - -- Zig debug configurations + after = function(_plugin) + local dap = require('dap') + dap.adapters.lldb = { type = 'executable', command = 'lldb-vscode', name = 'lldb' } dap.configurations.zig = { { - name = "Launch Zig Program", - type = "lldb", - request = "launch", + name = 'Launch Zig Program', + type = 'lldb', request = 'launch', program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/zig-out/bin/', 'file') end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - args = {}, - runInTerminal = false, + cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, runInTerminal = false, }, { - name = "Launch Zig Test", - type = "lldb", - request = "launch", + name = 'Launch Zig Test', + type = 'lldb', request = 'launch', program = function() return vim.fn.input('Path to test executable: ', vim.fn.getcwd() .. '/zig-cache/o/', 'file') end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - args = {}, + cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, { - name = "Attach to running Zig process", - type = "lldb", - request = "attach", - pid = function() - return require("dap.utils").pick_process({ filter = "zig" }) - end, + name = 'Attach to running Zig process', + type = 'lldb', request = 'attach', + pid = function() return require('dap.utils').pick_process({ filter = 'zig' }) end, cwd = '${workspaceFolder}', }, } end, }, + + ----------------------------------------------------------------- rust ------ { - "nvim-dap", + 'nvim-dap', for_cat = { cat = 'debug.rust', default = false }, - after = function(plugin) - local dap = require 'dap' + after = function(_plugin) + local dap = require('dap') + local codelldb_path = (require('nixCatsUtils').isNixCats and (nixCats('codelldb-path') or 'codelldb')) + or (vim.fn.stdpath('data') .. '/mason/packages/codelldb/extension/adapter/codelldb') - -- CodeLLDB adapter configuration for Rust - if require('nixCatsUtils').isNixCats then - local codelldb_path = nixCats("codelldb-path") or "codelldb" - dap.adapters.codelldb = { - type = 'server', - port = "${port}", - executable = { - command = codelldb_path, - args = {"--port", "${port}"}, - } - } - else - dap.adapters.codelldb = { - type = 'server', - port = "${port}", - executable = { - command = vim.fn.stdpath("data") .. "/mason/packages/codelldb/extension/adapter/codelldb", - args = {"--port", "${port}"}, - } - } - end + dap.adapters.codelldb = { + type = 'server', port = '${port}', + executable = { command = codelldb_path, args = { '--port', '${port}' } }, + } - -- Rust debug configurations dap.configurations.rust = { { - name = "Launch Rust Program (Debug)", - type = "codelldb", - request = "launch", - program = function() - return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/target/debug/', 'file') - end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - args = {}, + name = 'Launch Rust Program (Debug)', + type = 'codelldb', request = 'launch', + program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/target/debug/', 'file') end, + cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, { - name = "Launch Rust Program (Release)", - type = "codelldb", - request = "launch", - program = function() - return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/target/release/', 'file') - end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - args = {}, + name = 'Launch Rust Program (Release)', + type = 'codelldb', request = 'launch', + program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/target/release/', 'file') end, + cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, { - name = "Run Rust tests", - type = "codelldb", - request = "launch", - program = function() - return vim.fn.input('Path to test executable: ', vim.fn.getcwd() .. '/target/debug/', 'file') - end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - args = {}, + name = 'Run Rust tests', + type = 'codelldb', request = 'launch', + program = function() return vim.fn.input('Path to test executable: ', vim.fn.getcwd() .. '/target/debug/', 'file') end, + cwd = '${workspaceFolder}', stopOnEntry = false, args = {}, }, { - name = "Attach to running Rust process", - type = "codelldb", - request = "attach", - pid = function() - return require("dap.utils").pick_process() - end, + name = 'Attach to running Rust process', + type = 'codelldb', request = 'attach', + pid = function() return require('dap.utils').pick_process() end, cwd = '${workspaceFolder}', }, } + end, + }, - -- Configure CoreCLR (C#/.NET) debugging with netcoredbg - if nixCats('csharp') then - dap.adapters.coreclr = { - type = 'executable', - command = 'netcoredbg', - args = { '--interpreter=vscode' } - } - - dap.configurations.cs = { - { - name = "Launch .NET Core App", - type = "coreclr", - request = "launch", - program = function() - return vim.fn.input('Path to DLL: ', vim.fn.getcwd() .. '/bin/Debug/net6.0/', 'file') - end, - cwd = '${workspaceFolder}', - stopOnEntry = false, - }, - { - name = "Attach to .NET Process", - type = "coreclr", - request = "attach", - processId = function() - return require('dap.utils').pick_process() - end, - }, - } - end - - -- Configure JDWP (Java Debug Wire Protocol) debugging - if nixCats('java') then - -- Start Java app with: java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=5005 YourApp.jar - dap.adapters.java = { - type = 'server', - host = '127.0.0.1', - port = 5005, - enrich_config = function(config, on_config) - -- Additional configuration can be added here - on_config(config) - end, - } + ----------------------------------------------------------------- csharp ---- + { + 'nvim-dap', + for_cat = { cat = 'debug.csharp', default = false }, + after = function(_plugin) + local dap = require('dap') + dap.adapters.coreclr = { + type = 'executable', command = 'netcoredbg', args = { '--interpreter=vscode' }, + } + dap.configurations.cs = { + { + name = 'Launch .NET Core App', + type = 'coreclr', request = 'launch', + program = function() return vim.fn.input('Path to DLL: ', vim.fn.getcwd() .. '/bin/Debug/net6.0/', 'file') end, + cwd = '${workspaceFolder}', stopOnEntry = false, + }, + { + name = 'Attach to .NET Process', + type = 'coreclr', request = 'attach', + processId = function() return require('dap.utils').pick_process() end, + }, + } + end, + }, - dap.configurations.java = { - { - name = "Attach to Java Process", - type = "java", - request = "attach", - hostName = '127.0.0.1', - port = 5005, - preLaunchTask = nil, - }, - } - end + ----------------------------------------------------------------- java ------ + { + 'nvim-dap', + for_cat = { cat = 'debug.java', default = false }, + after = function(_plugin) + local dap = require('dap') + dap.adapters.java = { + type = 'server', host = '127.0.0.1', port = 5005, + enrich_config = function(config, on_config) on_config(config) end, + } + dap.configurations.java = { + { + name = 'Attach to Java Process', + type = 'java', request = 'attach', + hostName = '127.0.0.1', port = 5005, preLaunchTask = nil, + }, + } end, }, } diff --git a/lua/myLuaConf/format/init.lua b/lua/myLuaConf/format/init.lua index 5b059e6..f8493a7 100644 --- a/lua/myLuaConf/format/init.lua +++ b/lua/myLuaConf/format/init.lua @@ -1,54 +1,63 @@ +---Conform.nvim setup. Formatters per filetype come from the per-language +---Nix modules, exposed to lua via `nixCats.extra("languageConfig.formatters")`. +--- +---That tables already includes prettier(d) for json/html/css/yaml/etc., and +---is the single source of truth shared by every language package. + require('lze').load { { - "conform.nvim", + 'conform.nvim', for_cat = 'format', - -- cmd = { "" }, - -- event = "", - -- ft = "", keys = { - { "FF", desc = "[F]ormat [F]ile" }, + { 'FF', desc = '[F]ormat [F]ile' }, }, - -- colorscheme = "", - after = function (plugin) - local conform = require("conform") + after = function(_plugin) + local conform = require('conform') + + ---Pull the merged ft -> [tool] table from nix; fall back to a + ---reasonable hard-coded set if the config was loaded without nix. + local ft = nixCats.extra('languageConfig.formatters') or { + javascript = { 'prettierd', 'prettier' }, + javascriptreact = { 'prettierd', 'prettier' }, + typescript = { 'prettierd', 'prettier' }, + typescriptreact = { 'prettierd', 'prettier' }, + json = { 'prettierd', 'prettier' }, + jsonc = { 'prettierd', 'prettier' }, + html = { 'prettierd', 'prettier' }, + css = { 'prettierd', 'prettier' }, + scss = { 'prettierd', 'prettier' }, + yaml = { 'prettierd', 'prettier' }, + markdown = { 'prettierd', 'prettier' }, + } + ---Per-ft "stop after first" semantics for prettier(d) chains. + local formatters_by_ft = {} + for k, list in pairs(ft) do + if vim.tbl_contains(list, 'prettierd') or vim.tbl_contains(list, 'prettier') then + formatters_by_ft[k] = vim.list_extend({}, list) + formatters_by_ft[k].stop_after_first = true + else + formatters_by_ft[k] = list + end + end conform.setup({ - formatters_by_ft = { - -- NOTE: download some formatters in lspsAndRuntimeDeps - -- and configure them here - -- lua = { "stylua" }, - -- go = { "gofmt", "golint" }, - -- templ = { "templ" }, - -- Conform will run multiple formatters sequentially - -- python = { "isort", "black" }, - -- Use a sub-list to run only the first available formatter - javascript = { "prettierd", "prettier", stop_after_first = true }, - javascriptreact = { "prettierd", "prettier", stop_after_first = true }, - typescript = { "prettierd", "prettier", stop_after_first = true }, - typescriptreact = { "prettierd", "prettier", stop_after_first = true }, - zig = { "zigfmt" }, - rust = { "rustfmt" }, - r = { "styler" }, - rmd = { "styler" }, - qmd = { "styler" }, - }, + formatters_by_ft = formatters_by_ft, + ---format_on_save handles BufWritePre internally; no extra autocmd needed. format_on_save = { - timeout_ms = 500, - lsp_format = "fallback", + timeout_ms = 1000, + lsp_format = 'fallback', }, formatters = { zigfmt = { - command = "zig", - args = { "fmt", "--stdin" }, - stdin = true, + command = 'zig', + args = { 'fmt', '--stdin' }, + stdin = true, }, styler = { - command = "R", + command = 'R', args = { - "--slave", - "--no-restore", - "--no-save", - "-e", + '--slave', '--no-restore', '--no-save', + '-e', "con <- file('stdin'); styler::style_text(readLines(con)); close(con)", }, stdin = true, @@ -56,19 +65,9 @@ require('lze').load { }, }) - vim.keymap.set({ "n", "v" }, "FF", function() - conform.format({ - lsp_fallback = true, - async = false, - timeout_ms = 1000, - }) - end, { desc = "[F]ormat [F]ile" }) - vim.api.nvim_create_autocmd("BufWritePre", { - pattern = "*", - callback = function(args) - require("conform").format({ bufnr = args.buf }) - end, - }) + vim.keymap.set({ 'n', 'v' }, 'FF', function() + conform.format({ lsp_fallback = true, async = false, timeout_ms = 1000 }) + end, { desc = '[F]ormat [F]ile' }) end, }, } diff --git a/lua/myLuaConf/lint/init.lua b/lua/myLuaConf/lint/init.lua index 1e90c03..2844e1f 100644 --- a/lua/myLuaConf/lint/init.lua +++ b/lua/myLuaConf/lint/init.lua @@ -1,27 +1,27 @@ +---nvim-lint setup driven by nixCats per-language modules. Linters per +---filetype are merged from `nixCats.extra("languageConfig.linters")`, +---so adding a language only requires updating its nix module. + require('lze').load { { - "nvim-lint", + 'nvim-lint', for_cat = 'lint', - -- cmd = { "" }, - event = "FileType", - -- ft = "", - -- keys = "", - -- colorscheme = "", - after = function (plugin) - require('lint').linters_by_ft = { - -- NOTE: download some linters in lspsAndRuntimeDeps - -- and configure them here - -- markdown = {'vale',}, - -- javascript = { 'eslint' }, - -- typescript = { 'eslint' }, - r = { 'lintr' }, - rmd = { 'lintr' }, + event = { 'BufReadPost', 'BufNewFile' }, + after = function(_plugin) + local lint = require('lint') + + lint.linters_by_ft = nixCats.extra('languageConfig.linters') or { + javascript = { 'eslint_d' }, + javascriptreact = { 'eslint_d' }, + typescript = { 'eslint_d' }, + typescriptreact = { 'eslint_d' }, } - vim.api.nvim_create_autocmd({ "BufWritePost" }, { - callback = function() - require("lint").try_lint() - end, + ---Trigger linting on save, leaving insert mode and on file open. + local grp = vim.api.nvim_create_augroup('nixCats-nvim-lint', { clear = true }) + vim.api.nvim_create_autocmd({ 'BufWritePost', 'BufReadPost', 'InsertLeave' }, { + group = grp, + callback = function() lint.try_lint() end, }) end, }, diff --git a/lua/myLuaConf/opts_and_keys.lua b/lua/myLuaConf/opts_and_keys.lua index 27d18ed..b45b595 100644 --- a/lua/myLuaConf/opts_and_keys.lua +++ b/lua/myLuaConf/opts_and_keys.lua @@ -1,159 +1,133 @@ --- NOTE: These 2 need to be set up before any plugins are loaded. -vim.g.mapleader = ' ' +---These two MUST be set before any plugin is loaded. +vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' -if os.getenv('WAYLAND_DISPLAY') and vim.fn.exepath('wl-copy') ~= "" then +if os.getenv('WAYLAND_DISPLAY') and vim.fn.exepath('wl-copy') ~= '' then vim.g.clipboard = { - name = 'wl-clipboard', - copy = { - ['+'] = 'wl-copy', - ['*'] = 'wl-copy', - }, - paste = { - ['+'] = 'wl-paste', - ['*'] = 'wl-paste', - }, - cache_enabled = 1, + name = 'wl-clipboard', + copy = { ['+'] = 'wl-copy', ['*'] = 'wl-copy' }, + paste = { ['+'] = 'wl-paste', ['*'] = 'wl-paste' }, + cache_enabled = 1, } end --- [[ Setting options ]] --- See `:help vim.o` --- NOTE: You can change these options as you wish! - --- Sets how neovim will display certain whitespace characters in the editor. --- See `:help 'list'` --- and `:help 'listchars'` -vim.opt.list = true -vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' } - --- Set highlight on search -vim.opt.hlsearch = true -vim.keymap.set('n', '', 'nohlsearch') - --- Preview substitutions live, as you type! -vim.opt.inccommand = 'split' --- Minimal number of screen lines to keep above and below the cursor. -vim.opt.scrolloff = 10 +----------------------------------------------------------------- options ---- --- Make line numbers default -vim.wo.number = true +vim.opt.list = true +vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' } --- Enable mouse mode -vim.o.mouse = '' +vim.opt.hlsearch = true +vim.opt.inccommand = 'split' +vim.opt.scrolloff = 10 +vim.wo.number = true +vim.o.mouse = '' --- Indent --- vim.o.smarttab = true vim.opt.cpoptions:append('I') -vim.o.expandtab = true -vim.o.smartindent = true -vim.o.autoindent = true -vim.o.tabstop = 2 --- vim.o.softtabstop = 4 -vim.o.shiftwidth = 2 - --- stops line wrapping from being confusing -vim.o.breakindent = true +vim.o.expandtab = true +vim.o.smartindent = true +vim.o.autoindent = true +vim.o.tabstop = 2 +vim.o.shiftwidth = 2 +vim.o.breakindent = true --- Save undo history -vim.o.undofile = true +vim.o.undofile = true +vim.o.swapfile = false --- Toggle swapfile -vim.o.swapfile = false +vim.o.ignorecase = true +vim.o.smartcase = true --- Case-insensitive searching UNLESS \C or capital in search -vim.o.ignorecase = true -vim.o.smartcase = true +vim.wo.signcolumn = 'yes' +vim.wo.relativenumber = true --- Keep signcolumn on by default -vim.wo.signcolumn = 'yes' -vim.wo.relativenumber = true +vim.o.updatetime = 250 +vim.o.timeoutlen = 300 +vim.o.completeopt = 'menu,preview,noselect' +vim.o.termguicolors = true --- Decrease update time -vim.o.updatetime = 250 -vim.o.timeoutlen = 300 +---NOTE: We intentionally do NOT set vim.o.clipboard. The per-key y/p +---bindings below give explicit control over the system clipboard without +---clobbering the unnamed register on every delete/change. --- Set completeopt to have a better completion experience -vim.o.completeopt = 'menu,preview,noselect' +vim.g.netrw_liststyle = 0 +vim.g.netrw_banner = 0 --- NOTE: You should make sure your terminal supports this -vim.o.termguicolors = true +----------------------------------------------------------------- autocmds --- --- [[ Disable auto comment on enter ]] --- See :help formatoptions -vim.api.nvim_create_autocmd("FileType", { - desc = "remove formatoptions", +vim.api.nvim_create_autocmd('FileType', { + desc = 'remove autocomment formatoptions', callback = function() - vim.opt.formatoptions:remove({ "c", "r", "o" }) + vim.opt.formatoptions:remove({ 'c', 'r', 'o' }) end, }) --- [[ Highlight on yank ]] --- See `:help vim.highlight.on_yank()` local highlight_group = vim.api.nvim_create_augroup('YankHighlight', { clear = true }) vim.api.nvim_create_autocmd('TextYankPost', { + group = highlight_group, + pattern = '*', callback = function() - vim.highlight.on_yank() + ---vim.hl is the 0.11+ entry point; vim.highlight is deprecated. + local hl = vim.hl or vim.highlight + hl.on_yank() end, - group = highlight_group, - pattern = '*', }) -vim.g.netrw_liststyle=0 -vim.g.netrw_banner=0 --- [[ Basic Keymaps ]] - --- Keymaps for better default experience --- See `:help vim.keymap.set()` -vim.keymap.set("v", "J", ":m '>+1gv=gv", { desc = 'Moves Line Down' }) -vim.keymap.set("v", "K", ":m '<-2gv=gv", { desc = 'Moves Line Up' }) -vim.keymap.set("n", "", "zz", { desc = 'Scroll Down' }) -vim.keymap.set("n", "", "zz", { desc = 'Scroll Up' }) -vim.keymap.set("n", "n", "nzzzv", { desc = 'Next Search Result' }) -vim.keymap.set("n", "N", "Nzzzv", { desc = 'Previous Search Result' }) - -vim.keymap.set("n", "[", "bprev", { desc = 'Previous buffer' }) -vim.keymap.set("n", "]", "bnext", { desc = 'Next buffer' }) -vim.keymap.set("n", "l", "b#", { desc = 'Last buffer' }) -vim.keymap.set("n", "d", "bdelete", { desc = 'delete buffer' }) - --- see help sticky keys on windows +----------------------------------------------------------------- keymaps ---- + +vim.keymap.set('n', '', 'nohlsearch') + +vim.keymap.set('v', 'J', ":m '>+1gv=gv", { desc = 'Move line down' }) +vim.keymap.set('v', 'K', ":m '<-2gv=gv", { desc = 'Move line up' }) +vim.keymap.set('n', '', 'zz', { desc = 'Scroll down' }) +vim.keymap.set('n', '', 'zz', { desc = 'Scroll up' }) +vim.keymap.set('n', 'n', 'nzzzv', { desc = 'Next search result' }) +vim.keymap.set('n', 'N', 'Nzzzv', { desc = 'Previous search result' }) + +vim.keymap.set('n', '[', 'bprev', { desc = 'Previous buffer' }) +vim.keymap.set('n', ']', 'bnext', { desc = 'Next buffer' }) +vim.keymap.set('n', 'l', 'b#', { desc = 'Last buffer' }) +vim.keymap.set('n', 'd', 'bdelete', { desc = 'Delete buffer' }) + vim.cmd([[command! W w]]) vim.cmd([[command! Wq wq]]) vim.cmd([[command! WQ wq]]) vim.cmd([[command! Q q]]) --- Remap for dealing with word wrap vim.keymap.set('n', 'k', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true }) vim.keymap.set('n', 'j', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true }) --- Diagnostic keymaps -vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Go to previous diagnostic message' }) -vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Go to next diagnostic message' }) -vim.keymap.set('n', 'e', vim.diagnostic.open_float, { desc = 'Open floating diagnostic message' }) -vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Open diagnostics list' }) - +----------------------------------------------------------------- diagnostics --- kickstart.nvim starts you with this. --- But it constantly clobbers your system clipboard whenever you delete anything. +---vim.diagnostic.jump replaces goto_prev/goto_next in 0.11+. +local function diag_jump(count) + return function() vim.diagnostic.jump({ count = count, float = true }) end +end +vim.keymap.set('n', '[d', diag_jump(-1), { desc = 'Previous diagnostic' }) +vim.keymap.set('n', ']d', diag_jump(1), { desc = 'Next diagnostic' }) +vim.keymap.set('n', 'e', vim.diagnostic.open_float, { desc = 'Open floating diagnostic' }) +vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Diagnostics to loclist' }) + +vim.diagnostic.config({ + virtual_text = { prefix = '●', spacing = 2 }, + severity_sort = true, + underline = true, + update_in_insert = false, + float = { border = 'rounded', source = true }, + signs = true, +}) --- Sync clipboard between OS and Neovim. --- Remove this option if you want your OS clipboard to remain independent. --- See `:help 'clipboard'` -vim.o.clipboard = 'unnamed,unnamedplus' +----------------------------------------------------------------- clipboard -- --- You should instead use these keybindings so that they are still easy to use, but dont conflict -vim.keymap.set("n", 'y', '"+y', { noremap = true, silent = true, desc = 'Yank to clipboard' }) -vim.keymap.set({"v", "x"}, 'y', '"+y', { noremap = true, silent = true, desc = 'Yank to clipboard' }) -vim.keymap.set({"n", "v", "x"}, 'yy', '"+yy', { noremap = true, silent = true, desc = 'Yank line to clipboard' }) -vim.keymap.set({"n", "v", "x"}, 'Y', '"+yy', { noremap = true, silent = true, desc = 'Yank line to clipboard' }) -vim.keymap.set({"n", "v", "x"}, '', 'gg0vG$', { noremap = true, silent = true, desc = 'Select all' }) -vim.keymap.set({'n', 'v', 'x'}, 'p', '"+p', { noremap = true, silent = true, desc = 'Paste from clipboard' }) -vim.keymap.set('i', '', '+', { noremap = true, silent = true, desc = 'Paste from clipboard from within insert mode' }) -vim.keymap.set("x", "P", '"_dP', { noremap = true, silent = true, desc = 'Paste over selection without erasing unnamed register' }) +vim.keymap.set('n', 'y', '"+y', { noremap = true, silent = true, desc = 'Yank to clipboard' }) +vim.keymap.set({'v','x'}, 'y', '"+y', { noremap = true, silent = true, desc = 'Yank to clipboard' }) +vim.keymap.set({'n','v','x'}, 'yy', '"+yy', { noremap = true, silent = true, desc = 'Yank line to clipboard' }) +vim.keymap.set({'n','v','x'}, 'Y', '"+yy', { noremap = true, silent = true, desc = 'Yank line to clipboard' }) +vim.keymap.set({'n','v','x'}, '', 'gg0vG$', { noremap = true, silent = true, desc = 'Select all' }) +vim.keymap.set({'n','v','x'}, 'p', '"+p', { noremap = true, silent = true, desc = 'Paste from clipboard' }) +vim.keymap.set('i', '', '+', { noremap = true, silent = true, desc = 'Paste from clipboard (insert)' }) +vim.keymap.set('x', 'P', '"_dP', { noremap = true, silent = true, desc = 'Paste over selection (no register clobber)' }) +----------------------------------------------------------------- paths ------ --- Paths vim.keymap.set('n', 'yp', function() vim.fn.setreg('+', vim.fn.expand('%:p:.')) end, { desc = 'Copy file path (relative to cwd)' }) @@ -166,15 +140,12 @@ vim.keymap.set('n', 'yf', function() vim.fn.setreg('+', vim.fn.expand('%:t:r')) end, { desc = 'Copy file name without extension' }) --- Function to copy the paths of all buffers vim.keymap.set('n', 'ya', function() local paths = {} for _, buf in ipairs(vim.api.nvim_list_bufs()) do - if vim.api.nvim_buf_get_option(buf, 'buflisted') then + if vim.bo[buf].buflisted then local name = vim.api.nvim_buf_get_name(buf) - if name ~= '' then - table.insert(paths, name) - end + if name ~= '' then table.insert(paths, name) end end end vim.fn.setreg('+', table.concat(paths, '\n')) diff --git a/lua/myLuaConf/plugins/init.lua b/lua/myLuaConf/plugins/init.lua index 663b038..eecb06b 100644 --- a/lua/myLuaConf/plugins/init.lua +++ b/lua/myLuaConf/plugins/init.lua @@ -71,8 +71,35 @@ require('lze').load { { import = "myLuaConf.plugins.snacks", }, { import = "myLuaConf.plugins.ollama", }, { import = "myLuaConf.plugins.nerdtree", }, + { import = "myLuaConf.plugins.javascript", }, -- { import = "myLuaConf.plugins.claude-code", }, + { + "nvim-autopairs", + for_cat = 'general.always', + event = "InsertEnter", + after = function(_) + local ap = require('nvim-autopairs') + ap.setup({ + check_ts = true, + ts_config = { + lua = { 'string', 'source' }, + javascript = { 'string', 'template_string' }, + typescript = { 'string', 'template_string' }, + }, + disable_filetype = { 'TelescopePrompt', 'spectre_panel' }, + }) + ---Hook into nvim-cmp confirm so '(' is auto-completed for functions. + local ok_cmp, cmp = pcall(require, 'cmp') + if ok_cmp then + local ok_ext, ext = pcall(require, 'nvim-autopairs.completion.cmp') + if ok_ext then + cmp.event:on('confirm_done', ext.on_confirm_done()) + end + end + end, + }, + { "claude-code.nvim", for_cat = 'ai', @@ -81,6 +108,8 @@ require('lze').load { {"cc", "ClaudeCode", mode = {"n"}, noremap = true, desc = "Open Claude Code"}, }, after = function(plugin) + ---NOTE: isn't transmitted by most terminals (kitty/iterm/alacritty + ---without csi-u). We use cc to open and ct to toggle. require("claude-code").setup({ -- Terminal window settings window = { @@ -131,8 +160,8 @@ require('lze').load { -- Keymaps keymaps = { toggle = { - normal = "", -- Normal mode keymap for toggling Claude Code, false to disable - terminal = "", -- Terminal mode keymap for toggling Claude Code, false to disable + normal = "ct", -- terminal-safe alternative to + terminal = "ct", variants = { continue = "cC", -- Normal mode keymap for Claude Code with continue flag verbose = "cV", -- Normal mode keymap for Claude Code with verbose flag diff --git a/lua/myLuaConf/plugins/javascript.lua b/lua/myLuaConf/plugins/javascript.lua new file mode 100644 index 0000000..ee9b851 --- /dev/null +++ b/lua/myLuaConf/plugins/javascript.lua @@ -0,0 +1,120 @@ +---JavaScript / TypeScript-specific plugin specs. All gated on the `js` +---category so they only load for the jsvim package. + +return { + ----------------------------------------------------------------- jsx tags -- + { + 'nvim-ts-autotag', + for_cat = 'js', + event = { 'BufReadPre', 'BufNewFile' }, + after = function(_) + require('nvim-ts-autotag').setup({ + opts = { + enable_close = true, + enable_rename = true, + enable_close_on_slash = false, + }, + }) + end, + }, + + ----------------------------------------------------------------- jsx comments + { + 'nvim-ts-context-commentstring', + for_cat = 'js', + event = { 'BufReadPre', 'BufNewFile' }, + after = function(_) + ---Used by Comment.nvim via the pre_hook below. + require('ts_context_commentstring').setup({ + enable_autocmd = false, + }) + vim.g.skip_ts_context_commentstring_module = true + ---Re-wire Comment.nvim if it's already loaded. + local ok, comment = pcall(require, 'Comment') + if ok then + comment.setup({ + pre_hook = require('ts_context_commentstring.integrations.comment_nvim').create_pre_hook(), + }) + end + end, + }, + + ----------------------------------------------------------------- package.json + { + 'package-info.nvim', + for_cat = 'js', + ft = { 'json' }, + after = function(_) + require('package-info').setup({ + colors = { + up_to_date = '#3C4048', + outdated = '#d19a66', + }, + icons = { enable = true, style = { up_to_date = '| ', outdated = '| ' } }, + autostart = true, + hide_up_to_date = false, + hide_unstable_versions = false, + package_manager = 'npm', + }) + vim.keymap.set('n', 'ns', require('package-info').show, { desc = 'package.json: show versions' }) + vim.keymap.set('n', 'nh', require('package-info').hide, { desc = 'package.json: hide versions' }) + vim.keymap.set('n', 'nu', require('package-info').update, { desc = 'package.json: update package' }) + vim.keymap.set('n', 'nd', require('package-info').delete, { desc = 'package.json: delete package' }) + vim.keymap.set('n', 'ni', require('package-info').install, { desc = 'package.json: install new package' }) + vim.keymap.set('n', 'nc', require('package-info').change_version, { desc = 'package.json: change version' }) + end, + }, + + ----------------------------------------------------------------- neotest --- + { + 'neotest', + for_cat = 'js', + cmd = { 'Neotest' }, + keys = { + { 'tn', desc = 'Test: nearest' }, + { 'tf', desc = 'Test: file' }, + { 'ts', desc = 'Test: summary toggle' }, + { 'to', desc = 'Test: output' }, + { 'tw', desc = 'Test: watch file' }, + }, + load = function(name) + vim.cmd.packadd(name) + vim.cmd.packadd('neotest-jest') + vim.cmd.packadd('neotest-vitest') + end, + after = function(_) + local neotest = require('neotest') + neotest.setup({ + adapters = { + require('neotest-jest')({ + jestCommand = 'npx jest --', + jestConfigFile = function() + local file = vim.fn.expand('%:p') + if string.find(file, '/packages/') then + return string.match(file, '(.-/[^/]+/)src') .. 'jest.config.ts' + end + return vim.fn.getcwd() .. '/jest.config.ts' + end, + env = { CI = true }, + cwd = function() return vim.fn.getcwd() end, + }), + require('neotest-vitest'), + }, + }) + + vim.keymap.set('n', 'tn', function() neotest.run.run() end, { desc = 'Test: nearest' }) + vim.keymap.set('n', 'tf', function() neotest.run.run(vim.fn.expand('%')) end, { desc = 'Test: file' }) + vim.keymap.set('n', 'ts', function() neotest.summary.toggle() end, { desc = 'Test: summary toggle' }) + vim.keymap.set('n', 'to', function() neotest.output.open({ enter = true }) end, { desc = 'Test: output' }) + vim.keymap.set('n', 'tw', function() neotest.watch.toggle(vim.fn.expand('%')) end, { desc = 'Test: watch file' }) + end, + }, + + ----------------------------------------------------------------- SchemaStore + ---Loaded eagerly so jsonls/yamlls can read it during their setup. + { + 'SchemaStore.nvim', + for_cat = 'js', + event = 'DeferredUIEnter', + }, +} diff --git a/nix/languages/csharp.nix b/nix/languages/csharp.nix index 9035b09..edd2bfe 100644 --- a/nix/languages/csharp.nix +++ b/nix/languages/csharp.nix @@ -1,43 +1,37 @@ -# C# language configuration for sharpvim +## C# language module for sharpvim. { pkgs }: { - # LSP server and runtime dependencies lspsAndRuntimeDeps = with pkgs; [ omnisharp-roslyn + csharpier vimPlugins.omnisharp-extended-lsp-nvim ]; - # Debug adapter debug = with pkgs; [ netcoredbg ]; - # Code formatter - formatter = with pkgs; [ - csharpier - ]; + formatters = { + cs = [ "csharpier" ]; + }; - # Linter - linter = "roslyn"; + linters = {}; + + treesitter = [ "c_sharp" ]; - # Package naming packageName = "sharpvim"; - appName = "sharpvim"; + appName = "sharpvim"; + lspName = "omnisharp"; - # ASCII art logo logo = '' ███████╗██╗ ██╗ █████╗ ██████╗ ██████╗ ██╗ ██╗██╗███╗ ███╗ ██╔════╝██║ ██║██╔══██╗██╔══██╗██╔══██╗██║ ██║██║████╗ ████║ ███████╗███████║███████║██████╔╝██████╔╝██║ ██║██║██╔████╔██║ -╚════██║██╔══██║██╔══██║██╔══██╗██╔═══╝ ╚██╗ ██╔╝██║██║╚██╔╝██║ +╚════██║██╔══██║██╔══██║██╔══██╗██╔═══╝ ╚██╗ ██╔╝██║██║╚██╔╝██║ ███████║██║ ██║██║ ██║██║ ██║██║ ╚████╔╝ ██║██║ ╚═╝ ██║ ╚══════╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - # LSP server name (as used in lspconfig) - lspName = "omnisharp"; - - # Additional paths for sharpvim ls-path = "${pkgs.omnisharp-roslyn.outPath}/bin/OmniSharp"; } diff --git a/nix/languages/java.nix b/nix/languages/java.nix index dabd3a9..68b47c9 100644 --- a/nix/languages/java.nix +++ b/nix/languages/java.nix @@ -1,29 +1,27 @@ -# Java language configuration for jvim +## Java language module for jvim. { pkgs }: { - # LSP server and runtime dependencies lspsAndRuntimeDeps = with pkgs; [ jdt-language-server + google-java-format vimPlugins.nvim-jdtls ]; - # Debug adapter debug = with pkgs; [ ]; - # Code formatter - formatter = with pkgs; [ - google-java-format - ]; + formatters = { + java = [ "google-java-format" ]; + }; - # Linter - linter = "checkstyle"; + linters = {}; + + treesitter = [ "java" ]; - # Package naming packageName = "jvim"; - appName = "jvim"; + appName = "jvim"; + lspName = "jdtls"; - # ASCII art logo logo = '' ██╗██╗ ██╗██╗███╗ ███╗ ██║██║ ██║██║████╗ ████║ @@ -33,9 +31,5 @@ ╚════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - # LSP server name (as used in lspconfig) - lspName = "jdtls"; - - # Additional paths for jvim ls-path = "${pkgs.jdt-language-server.outPath}/bin/jdtls"; } diff --git a/nix/languages/javascript.nix b/nix/languages/javascript.nix index 5956f67..b44f5cc 100644 --- a/nix/languages/javascript.nix +++ b/nix/languages/javascript.nix @@ -1,30 +1,62 @@ -# JavaScript/TypeScript language configuration for jsvim +## JavaScript / TypeScript language module for jsvim. { pkgs }: { - # LSP server and runtime dependencies + ## Tools available on PATH inside the wrapped Neovim. + ## Includes: LSPs, formatters, linters, and the runtime (node) the LSPs + ## themselves shell out to. lspsAndRuntimeDeps = with pkgs; [ + nodejs_22 + typescript typescript-language-server + ## eslint / jsonls / cssls / html LSPs all live here: + vscode-langservers-extracted + tailwindcss-language-server + emmet-language-server + yaml-language-server + eslint_d + prettierd ]; - # Debug adapter + ## Debug adapters. debug = with pkgs; [ vscode-js-debug ]; - # Code formatter - formatter = with pkgs; [ - prettierd - ]; + ## conform.nvim formatters_by_ft contributions. + formatters = { + javascript = [ "prettierd" "prettier" ]; + javascriptreact = [ "prettierd" "prettier" ]; + typescript = [ "prettierd" "prettier" ]; + typescriptreact = [ "prettierd" "prettier" ]; + json = [ "prettierd" "prettier" ]; + jsonc = [ "prettierd" "prettier" ]; + html = [ "prettierd" "prettier" ]; + css = [ "prettierd" "prettier" ]; + scss = [ "prettierd" "prettier" ]; + less = [ "prettierd" "prettier" ]; + yaml = [ "prettierd" "prettier" ]; + markdown = [ "prettierd" "prettier" ]; + graphql = [ "prettierd" "prettier" ]; + vue = [ "prettierd" "prettier" ]; + svelte = [ "prettierd" "prettier" ]; + }; - # Linter - linter = "eslint"; + ## nvim-lint linters_by_ft contributions. + linters = { + javascript = [ "eslint_d" ]; + javascriptreact = [ "eslint_d" ]; + typescript = [ "eslint_d" ]; + typescriptreact = [ "eslint_d" ]; + }; + + ## Treesitter parsers (informational; we use withAllGrammars). + treesitter = [ "javascript" "typescript" "tsx" "jsdoc" "json" "json5" "html" "css" "yaml" "graphql" ]; - # Package naming packageName = "jsvim"; - appName = "jsvim"; + appName = "jsvim"; + lspName = "ts_ls"; - # ASCII art logo logo = '' ██╗ ███████╗ ██╗ ██╗██╗███╗ ███╗ ██║ ╚═══ ██║ ██║ ██║██║████╗ ████║ @@ -34,9 +66,6 @@ ╚════╝ ╚══════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - # LSP server name (as used in lspconfig) - lspName = "ts_ls"; - - # Additional paths for jsvim + ## Path to the vscode-js-debug DAP server entry point. js-debug-path = "${pkgs.vscode-js-debug.outPath}/lib/node_modules/js-debug/dist/src/dapDebugServer.js"; } diff --git a/nix/languages/r.nix b/nix/languages/r.nix index 1c2c8d9..22f9206 100644 --- a/nix/languages/r.nix +++ b/nix/languages/r.nix @@ -1,8 +1,7 @@ -# R language configuration for rvim +## R language module for rvim. { pkgs }: { - # LSP server and runtime dependencies lspsAndRuntimeDeps = with pkgs; [ R rPackages.languageserver @@ -10,22 +9,27 @@ rPackages.lintr ]; - # Debug adapter debug = with pkgs; [ ]; - # Code formatter - formatter = with pkgs; [ - # R formatter (styler is configured via R command in Lua config) - ]; + ## styler is invoked through R via a shim formatter defined in + ## lua/myLuaConf/format/init.lua's `formatters = { styler = ... }`. + formatters = { + r = [ "styler" ]; + rmd = [ "styler" ]; + qmd = [ "styler" ]; + }; + + linters = { + r = [ "lintr" ]; + rmd = [ "lintr" ]; + }; - # Linter - linter = "lintr"; + treesitter = [ "r" ]; - # Package naming packageName = "rvim"; - appName = "rvim"; + appName = "rvim"; + lspName = "r_language_server"; - # ASCII art logo logo = '' ██████╗ ██╗ ██╗██╗███╗ ███╗ ██╔══██╗██║ ██║██║████╗ ████║ @@ -34,7 +38,4 @@ ██║ ██║ ╚████╔╝ ██║██║ ╚═╝ ██║ ╚═╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - - # LSP server name (as used in lspconfig) - lspName = "r_language_server"; } diff --git a/nix/languages/rust.nix b/nix/languages/rust.nix index 916c93b..0c60047 100644 --- a/nix/languages/rust.nix +++ b/nix/languages/rust.nix @@ -1,8 +1,7 @@ -# Rust language configuration for rustvim +## Rust language module for rustvim. { pkgs }: { - # LSP server and runtime dependencies lspsAndRuntimeDeps = with pkgs; [ rust-analyzer cargo @@ -10,24 +9,24 @@ rustfmt ]; - # Debug adapter debug = with pkgs; [ vscode-extensions.vadimcn.vscode-lldb ]; - # Code formatter - formatter = with pkgs; [ - rustfmt - ]; + formatters = { + rust = [ "rustfmt" ]; + }; - # Linter - linter = "clippy"; + ## clippy diagnostics come via rust-analyzer's `check.command = "clippy"`, + ## so we don't run a separate nvim-lint linter for rust. + linters = {}; + + treesitter = [ "rust" ]; - # Package naming packageName = "rustvim"; - appName = "rustvim"; + appName = "rustvim"; + lspName = "rust_analyzer"; - # ASCII art logo logo = '' ██████╗ ██╗ ██╗███████╗████████╗██╗ ██╗██╗███╗ ███╗ ██╔══██╗██║ ██║██╔════╝╚══██╔══╝██║ ██║██║████╗ ████║ @@ -37,10 +36,6 @@ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝ ╚═╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - # LSP server name (as used in lspconfig) - lspName = "rust_analyzer"; - - # Additional paths for rustvim - ls-path = "${pkgs.rust-analyzer.outPath}/bin/rust-analyzer"; + ls-path = "${pkgs.rust-analyzer.outPath}/bin/rust-analyzer"; codelldb-path = "${pkgs.vscode-extensions.vadimcn.vscode-lldb.outPath}/share/vscode/extensions/vadimcn.vscode-lldb-*/adapter/codelldb"; } diff --git a/nix/languages/zig.nix b/nix/languages/zig.nix index 4441a27..7e6ca3d 100644 --- a/nix/languages/zig.nix +++ b/nix/languages/zig.nix @@ -1,30 +1,28 @@ -# Zig language configuration for zvim +## Zig language module for zvim. { pkgs }: { - # LSP server and runtime dependencies lspsAndRuntimeDeps = with pkgs; [ zls + zig ]; - # Debug adapter debug = with pkgs; [ lldb ]; - # Code formatter - formatter = with pkgs; [ - # Zig's formatter is built-in (zig fmt) - ]; + formatters = { + zig = [ "zigfmt" ]; + }; + + linters = {}; - # Linter - linter = ""; + treesitter = [ "zig" ]; - # Package naming packageName = "zvim"; - appName = "zvim"; + appName = "zvim"; + lspName = "zls"; - # ASCII art logo logo = '' ███████╗██╗ ██╗██╗███╗ ███╗ ╚══███╔╝██║ ██║██║████╗ ████║ @@ -34,9 +32,5 @@ ╚══════╝ ╚═══╝ ╚═╝╚═╝ ╚═╝ ''; - # LSP server name (as used in lspconfig) - lspName = "zls"; - - # Additional paths for zvim ls-path = "${pkgs.zls.outPath}/bin/zls"; } diff --git a/nix/lib.nix b/nix/lib.nix index e475758..6cfd6c6 100644 --- a/nix/lib.nix +++ b/nix/lib.nix @@ -1,153 +1,119 @@ -# Helper functions and utilities for managing language packages -# This module provides functions for building language-specific packages -# and managing their configurations consistently. +## Helper functions for building per-language Neovim packages. +## +## Each language module under ./languages/ exports a single attrset. +## Required fields (see ./languages/javascript.nix for the canonical example): +## lspsAndRuntimeDeps :: [pkg] Tools (LSPs, formatters, linters, node, ...) +## that should be on PATH inside Neovim. +## debug :: [pkg] Debug adapters (vscode-js-debug, lldb, ...). +## formatters :: { ft = [str]; ... } +## conform.nvim-style ft -> formatter list. +## linters :: { ft = [str]; ... } +## nvim-lint-style ft -> linter list. +## lspName :: string The lspconfig server name (e.g. "ts_ls"). +## packageName :: string The flake output name (e.g. "jsvim"). +## appName :: string vim.g.appName-style identifier. +## logo :: string ASCII logo string for dashboards. +## +## Optional fields: +## ls-path :: string Absolute path to the language server bin. +## codelldb-path :: string Path to the codelldb adapter (rust). +## js-debug-path :: string Path to vscode-js-debug dapDebugServer.js. +## treesitter :: [str] Treesitter parsers (informational). +## extraCategories :: { ... } Additional categories to enable on the +## package built from this module. +## extraSettings :: { ... } Additional values for `settings`. +## extra :: { ... } Additional values merged into nixCats `extra`. -{ - # mkLanguagePackage: Helper function to create a language-specific Neovim package - # - # This function standardizes the creation of language packages by providing - # consistent defaults and reducing boilerplate in flake.nix. - # - # Arguments: - # language: string - The language category name (e.g., "rust", "javascript") - # appName: string - The application name (e.g., "rustvim", "jsvim") - # logo: string - ASCII art logo for the package (optional) - # extraCategories: set - Additional categories to enable (optional) - # colorscheme: string - Color scheme to use (default: "onedark") - # - # Returns: - # A package definition set compatible with nixCats packageDefinitions - # - # Example: - # rustvim = mkLanguagePackage { - # language = "rust"; - # appName = "rustvim"; - # logo = "(import ./nix/languages/rust.nix { inherit pkgs; }).logo"; - # }; +{ nixpkgs }: + +let + lib = nixpkgs.lib; +in +rec { + ## Import every language module once. + languageModules = pkgs: { + javascript = import ./languages/javascript.nix { inherit pkgs; }; + java = import ./languages/java.nix { inherit pkgs; }; + csharp = import ./languages/csharp.nix { inherit pkgs; }; + rust = import ./languages/rust.nix { inherit pkgs; }; + zig = import ./languages/zig.nix { inherit pkgs; }; + r = import ./languages/r.nix { inherit pkgs; }; + }; + + availableLanguages = [ "javascript" "java" "csharp" "rust" "zig" "r" ]; + + ## Merge the per-language `formatters` / `linters` tables into a single + ## ft -> [tool] map, suitable for conform / nvim-lint. + ## + ## Later languages overwrite earlier ones if they declare the same ft. + mergeFt = field: langs: + lib.foldl' (acc: lang: + acc // (lang.${field} or {}) + ) {} (lib.attrValues langs); + + ## Build a combined `extra.languageConfig` set that the lua side reads + ## via `nixCats.extra("languageConfig.formatters")` etc. + ## + ## We pass the FULL set of language formatters/linters to every package; + ## the lua side will only actually invoke tools that are on PATH (i.e. + ## that come from a category the package enabled). + mkLanguageConfig = pkgs: { + formatters = mergeFt "formatters" (languageModules pkgs); + linters = mergeFt "linters" (languageModules pkgs); + }; + + ## Build a single language package definition. + ## + ## Arguments: + ## language - Key into `languageModules` (e.g. "javascript"). + ## aliases - Extra binary aliases the wrapper installs. + ## colorscheme - "onedark" | "catppuccin" | "tokyonight" | ... + ## extraCategories - Additional categories to set true on this package. + ## extraSettings - Additional `settings` entries (merged after defaults). mkLanguagePackage = { language, - appName, - logo ? "", - extraCategories ? {}, + aliases ? [], colorscheme ? "onedark", - }: { pkgs, ... }@misc: { + extraCategories ? {}, + extraSettings ? {}, + }: { pkgs, ... }@misc: + let + lang = (languageModules pkgs).${language}; + in { settings = { - wrapRc = true; + wrapRc = true; configDirName = "mox-nvim"; - aliases = []; - }; + inherit aliases; + } // extraSettings; categories = { - markdown = true; - general = true; - lint = true; - format = true; - neonixdev = true; - test = { - subtest1 = true; - }; + markdown = true; + general = true; + lint = true; + format = true; + neonixdev = true; + ai = false; ## opt-in per package lspDebugMode = false; - themer = true; + themer = true; colorscheme = colorscheme; - appName = appName; - inherit logo; - } // { - "${language}" = true; - } // extraCategories; - - extra = { - nixdExtras = { - nixpkgs = nixpkgs; - }; - }; - }; - - # loadLanguageModule: Helper function to import a language module - # - # This function consistently loads language module files from the - # nix/languages/ directory with proper error handling. - # - # Arguments: - # languageName: string - The name of the language (e.g., "rust") - # pkgs: set - The nixpkgs set - # - # Returns: - # The language module containing lspsAndRuntimeDeps, debug, formatter, etc. - # - # Example: - # rust = (loadLanguageModule "rust" pkgs).lspsAndRuntimeDeps; - loadLanguageModule = languageName: pkgs: - import (./languages/${languageName}.nix) { inherit pkgs; }; - # languageModules: Convenience set for all available language modules - # - # Usage: - # languages = lib.languageModules pkgs; - # rust_lsp = languages.rust.lspsAndRuntimeDeps; - languageModules = pkgs: { - javascript = import ./languages/javascript.nix { inherit pkgs; }; - java = import ./languages/java.nix { inherit pkgs; }; - csharp = import ./languages/csharp.nix { inherit pkgs; }; - rust = import ./languages/rust.nix { inherit pkgs; }; - zig = import ./languages/zig.nix { inherit pkgs; }; - r = import ./languages/r.nix { inherit pkgs; }; - }; + appName = lang.appName; + logo = lang.logo; + lspName = lang.lspName; - # extractLanguageData: Helper to extract common fields from language modules - # - # This function extracts all the language module data and organizes - # it into categoryDefinitions-compatible structures. - # - # Arguments: - # languages: set - The result of languageModules - # - # Returns: - # A set containing lspsAndRuntimeDeps, debug, formatters, and linters - # organized by language category - extractLanguageData = languages: { - lspsAndRuntimeDeps = builtins.mapAttrs - (name: lang: lang.lspsAndRuntimeDeps) - languages; + test = { subtest1 = true; }; + } + ## Enable the language category itself (e.g. js, rust, zig). + // (let key = if language == "javascript" then "js" else language; in { ${key} = true; }) + ## Optional language-specific paths. + // lib.optionalAttrs (lang ? ls-path) { ls-path = lang.ls-path; } + // lib.optionalAttrs (lang ? codelldb-path) { codelldb-path = lang.codelldb-path; } + // lib.optionalAttrs (lang ? js-debug-path) { js-debug-path = lang.js-debug-path; } + // extraCategories; - debug = builtins.mapAttrs - (name: lang: lang.debug) - languages; - - formatters = builtins.mapAttrs - (name: lang: lang.formatter) - languages; - - linters = builtins.mapAttrs - (name: lang: lang.linter) - languages; + extra = { + nixdExtras = { inherit nixpkgs; }; + languageConfig = mkLanguageConfig pkgs; + }; }; - - # availableLanguages: List all configured languages - availableLanguages = [ - "javascript" - "java" - "csharp" - "rust" - "zig" - "r" - ]; - - # getLanguageInfo: Get metadata about a language - # - # Arguments: - # language: string - The language name - # languageModules: set - The language modules set - # - # Returns: - # A set with packageName, appName, lspName, and logo - getLanguageInfo = language: languageModules: - let - mod = languageModules."${language}"; - in - if builtins.hasAttr language languageModules then - { - inherit (mod) packageName appName lspName logo; - } - else - throw "Unknown language: ${language}"; }