diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 00000000000..be47d4a5761 --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,5 @@ +{ + "enabledPlugins": { + "lua-lsp@claude-plugins-official": true + } +} diff --git a/.claude/settings.local.json b/.claude/settings.local.json new file mode 100644 index 00000000000..a8c9ff87232 --- /dev/null +++ b/.claude/settings.local.json @@ -0,0 +1,8 @@ +{ + "permissions": { + "allow": [ + "Bash(wc:*)", + "Bash(git:*)" + ] + } +} diff --git a/.githooks/post-commit b/.githooks/post-commit new file mode 100755 index 00000000000..fb8fa665cee --- /dev/null +++ b/.githooks/post-commit @@ -0,0 +1,8 @@ +#!/bin/sh +echo "→ Pushing to remote..." +if git push; then + echo "✓ Push successful." +else + echo "✗ Push failed. Run 'git push' manually to retry." >&2 + exit 1 +fi diff --git a/.gitignore b/.gitignore index 68486fc3d35..3187bdad38f 100644 --- a/.gitignore +++ b/.gitignore @@ -10,5 +10,4 @@ spell/ # For the official `nvim-lua/kickstart.nvim` git repository, we leave it ignored to avoid unneeded # merge conflicts. lazy-lock.json - .DS_Store diff --git a/MANUAL.md b/MANUAL.md new file mode 100644 index 00000000000..cc0b3992e72 --- /dev/null +++ b/MANUAL.md @@ -0,0 +1,341 @@ +# Neovim Field Manual + +This is a hands-on guide for this config. It focuses on the shortcuts, motions, +and working habits that are most useful day to day. + +Leader is ``. + +## First Moves + +| Goal | Keys | Notes | +| --- | --- | --- | +| See available leader groups | `` | `which-key` opens immediately. | +| Find files | `sf` | Telescope file picker. | +| Search text in project | `sg` | Uses ripgrep through Telescope. | +| Search current buffer | `/` | Compact in-buffer fuzzy search. | +| Switch buffers | `` | Telescope buffer picker. | +| Previous / next buffer | `` / `` | Bufferline navigation. | +| Reveal file tree | `\` | Neo-tree reveal. | +| Open Oil explorer | `eo` | Editable directory buffer. | +| Format buffer | `f` | Conform, with LSP fallback. | +| Exit terminal mode | `` | Easier than ``. | + +## Motions Worth Practicing + +### Cursor and file movement + +| Motion | Meaning | Practice | +| --- | --- | --- | +| `h j k l` | left, down, up, right | Use these before arrow keys. | +| `w` / `b` | next / previous word start | Move through identifiers quickly. | +| `e` / `ge` | next / previous word end | Useful before `c` and `d`. | +| `0` / `^` / `$` | line start, first nonblank, line end | Combine with `d`, `y`, `c`. | +| `gg` / `G` | file top / bottom | Prefix `G` with a line number. | +| `%` | matching pair | Parentheses, braces, brackets. | +| `{` / `}` | paragraph or block-ish movement | Good in Markdown and code blocks. | +| `` / `` | half-page down / up | Your `scrolloff=10` keeps context visible. | +| `` / `` | jump back / forward | `jumpoptions=clean,view` restores view better. | + +### Search movement + +| Motion | Meaning | +| --- | --- | +| `/text` | search forward | +| `?text` | search backward | +| `n` / `N` | next / previous match | +| `*` / `#` | search word under cursor forward / backward | +| `` | clear search highlight | + +Search is smart-case: lowercase searches are case-insensitive, mixed-case +searches become case-sensitive. + +### One-character targeting + +| Motion | Meaning | Example | +| --- | --- | --- | +| `f` | move to next char | `f)` | +| `F` | move to previous char | `F(` | +| `t` | move before next char | `dt,` deletes until comma. | +| `T` | move after previous char | `cT.` changes back to after dot. | +| `;` / `,` | repeat char search forward / backward | Works after `f`, `F`, `t`, `T`. | + +## Editing Grammar + +Vim editing is usually: + +```text +operator + motion +``` + +| Operator | Meaning | Example | +| --- | --- | --- | +| `d` | delete | `dw`, `d$`, `di"` | +| `c` | change | `ciw`, `cib`, `ct,` | +| `y` | yank | `yiw`, `yap`, `y$` | +| `>` / `<` | indent / unindent | `>ip`, `sh` | +| Keymaps | `sk` | +| Files | `sf` | +| Builtin pickers | `ss` | +| Word under cursor | `sw` | +| Live grep | `sg` | +| Diagnostics | `sd` | +| Resume last picker | `sr` | +| Recent files | `s.` | +| Commands | `sc` | +| Open-file grep | `s/` | +| Neovim config files | `sn` | +| Project search and replace | `sR` | + +Hands-on flow: + +1. Use `sg` to find a symbol or string. +2. Use `` in Telescope when you want a quickfix list. +3. Use `sR` for real project edits with preview. +4. Use `f` after edits to format. + +## LSP and Code Intelligence + +| Goal | Keys | +| --- | --- | +| Rename symbol | `grn` | +| Code action | `gra` | +| References | `grr` or `gr` | +| Implementation | `gri` or `gi` | +| Definition | `grd` or `gd` | +| Declaration | `grD` or `gD` | +| Document symbols | `gO` | +| Workspace symbols | `gW` | +| Type definition | `grt` or `gt` | +| Toggle inlay hints | `th` | + +This config also enables LSP CodeLens when the server supports it and linked +editing for servers that expose `textDocument/linkedEditingRange`. + +## Diagnostics + +| Goal | Keys | +| --- | --- | +| Previous diagnostic | `[d` | +| Next diagnostic | `]d` | +| Diagnostic details | `de` | +| Diagnostics location list | `q` | +| Yank diagnostic context | `dy` | +| Trouble all diagnostics | `xx` | +| Trouble workspace diagnostics | `xw` | +| Trouble current document | `xd` | +| Trouble quickfix | `xq` | +| Trouble loclist | `xl` | + +Diagnostic display is intentionally quieter: + +- Virtual text shows warnings and errors. +- Underlines are limited to errors. +- Diagnostic floats use rounded borders. +- Jumping diagnostics opens a focused-on-cursor float through `jump.on_jump`. + +## Git + +### Repository-level UI + +| Goal | Keys | +| --- | --- | +| Open Neogit | `Gg` | +| Open Diffview | `Gd` | +| Close Diffview | `GD` | +| Current file history | `Gf` | +| Repository history | `GF` | + +### Hunk-level work + +| Goal | Keys | +| --- | --- | +| Next / previous hunk | `]c` / `[c` | +| Stage hunk | `hs` | +| Reset hunk | `hr` | +| Stage buffer | `hS` | +| Undo staged hunk | `hu` | +| Reset buffer | `hR` | +| Preview hunk | `hp` | +| Preview hunk inline | `hi` | +| Blame current line | `hb` | +| Diff against index | `hd` | +| Diff against last commit | `hD` | +| All hunks to quickfix | `hQ` | +| Current buffer hunks to quickfix | `hq` | +| Toggle current-line blame | `tb` | +| Toggle word diff | `tw` | +| Toggle deleted lines | `tD` | + +In visual mode, `hs` and `hr` stage or reset the selected hunk +range. + +## Buffers, Windows, and Sessions + +| Goal | Keys | +| --- | --- | +| Previous / next buffer | `` / `` | +| Pick buffer | `bp` | +| Delete current buffer | `bd` | +| Delete all listed buffers | `bD` | +| Move left / right window | `wh` / `wl` | +| Move lower / upper window | `wj` / `wk` | +| Tmux-aware navigation | `` | +| Restore session | `wr` | +| Restore last session | `wl` | +| Stop session save | `wd` | + +Splits open to the right and below by default. + +## Files and AI-Friendly References + +| Goal | Keys | +| --- | --- | +| Neo-tree reveal | `\` | +| Oil explorer | `eo` | +| Harpoon add file | `a` | +| Harpoon menu | `hm` or `` | +| Harpoon previous / next | `` / `` | +| Yank absolute file location | `ya` | +| Yank relative file location | `yr` | +| Yank current file as `@path` | `yf` | +| Yank current directory as `@dir` | `yd` | + +The reference yanks include file, range, and symbol context when available. +They are useful for prompts, review comments, and issue descriptions. + +## Tests and Debugging + +### Neotest + +| Goal | Keys | +| --- | --- | +| Run nearest test | `nr` | +| Run current file | `nf` | +| Run suite from cwd | `ns` | +| Debug nearest test | `nd` | +| Toggle summary | `nn` | +| Open output | `no` | +| Toggle output panel | `nO` | +| Attach | `na` | +| Stop run | `nS` | + +### DAP + +| Goal | Keys | +| --- | --- | +| Continue / start | `` | +| Step into | `` | +| Step over | `` | +| Step out | `` | +| Toggle breakpoint | `b` | +| Conditional breakpoint | `B` | +| Toggle DAP UI | `` | + +Configured adapters include Go through `dap-go` when `dlv` exists, JS/TS through +`js-debug-adapter`, and C/C++ through `codelldb`. + +## CMake, Markdown, and Code Context + +| Goal | Keys | +| --- | --- | +| CMake generate | `cg` | +| CMake build | `cb` | +| CMake run | `cr` | +| CMake test | `ct` | +| CMake config | `cc` | +| Markdown preview | `mp` | +| Toggle Treesitter context | `tc` | +| Next function start | `jm` | +| Next function end | `jM` | +| Previous function start | `jk` | +| Previous function end | `jK` | +| Next class start | `jc` | +| Previous class start | `jC` | +| Open all folds | `zR` | +| Close all folds | `zM` | + +## Completion and Snippets + +Completion uses `blink.cmp` with LSP, paths, and snippets. + +| Goal | Keys | +| --- | --- | +| Open completion/docs | `` | +| Next / previous item | `` / `` | +| Accept completion | `` | +| Hide menu | `` | +| Toggle signature help | `` | + +Supermaven is active on insert: + +| Goal | Keys | +| --- | --- | +| Accept suggestion | `` | +| Clear suggestion | `` | +| Accept word | `` | + +## Config-Specific Habits + +- Use `` groups by memory shape: `s` for search, `g` for goto, `G` for + git UI, `h` for git hunks, `x` for Trouble, `n` for tests, `w` for windows + and sessions, `y` for reference yanks. +- Keep relative line numbers on. For example, `d5j` deletes five lines down, + and `y3k` yanks three lines up. +- Use `.` after focused edits. Example: `ciwnew_name` then jump and press + `.` to repeat. +- Prefer text objects over visual selection when possible: `ci"`, `di)`, + `yaf`, `dac`. +- Use `` after LSP jumps. Your jump list preserves view, so returning to + the previous context is less disorienting. +- Use `dy`, `yr`, and `yf` when copying context for AI + agents or code reviews. +- Do not rely on modelines for project settings. This config disables modelines + for safer project-local behavior. + +## Five-Minute Drill + +1. Open a project with `nvim .`. +2. Press `sf`, open a file. +3. Use `/`, `n`, `N`, `w`, `b`, `%`, ``, and `` to move without the mouse. +4. Change a word with `ciw`, repeat the edit elsewhere with `.`. +5. Select a function with `vaf`, then try `yaf` and `daf` in a scratch file. +6. Jump to definition with `gd`, return with ``. +7. Open diagnostics with `xx`, then jump with `[d` and `]d`. +8. Preview a git hunk with `hp`, then stage it with `hs`. +9. Run the nearest test with `nr`. +10. Copy an AI-friendly reference with `yr`. diff --git a/README.md b/README.md index 093e42a6dd9..19f022d51d1 100644 --- a/README.md +++ b/README.md @@ -1,19 +1,33 @@ -# kickstart.nvim +# nvim -## Introduction +Personal Neovim config based on `kickstart.nvim`, tuned for backend-heavy work +in JavaScript/TypeScript, C/C++, Kubernetes, and server development. -A starting point for Neovim that is: +For the hands-on shortcut and motion guide, start with [MANUAL.md](MANUAL.md). -* Small -* Single-file -* Completely Documented +This README is the working manual for what is configured, why it exists, and +how to use it quickly. -**NOT** a Neovim distribution, but instead a starting point for your configuration. +## Design principles -## Installation +- Additive, not disruptive: new plugins and mappings are added without replacing + existing behavior. +- Modular plugin specs: each concern lives in `lua/custom/plugins/*.lua`. +- Terminal-friendly workflow: most actions map to short leader sequences and + preserve CLI-first habits. +- Keep startup stable: major features are lazy-loaded by command, filetype, or + explicit keymaps where possible. -### Install Neovim +## Repository layout +- `init.lua`: base options, core plugin setup, LSP, formatting, treesitter. +- `lua/custom/plugins/*.lua`: modular plugin specs and custom behavior. +- `doc/nvim.txt`: Vim help document (`:help nvim-config`). +- `lazy-lock.json`: plugin lockfile managed by lazy.nvim. + +## Quick validation commands + +Run these after config changes: Kickstart.nvim targets *only* the latest ['stable'](https://github.com/neovim/neovim/releases/tag/stable) and latest ['nightly'](https://github.com/neovim/neovim/releases/tag/nightly) of Neovim. @@ -82,114 +96,107 @@ too - it's ignored in the kickstart repo to make maintenance easier, but it's
Linux and Mac ```sh -git clone https://github.com/nvim-lua/kickstart.nvim.git "${XDG_CONFIG_HOME:-$HOME/.config}"/nvim -``` - -
- -
Windows - -If you're using `cmd.exe`: - -``` -git clone https://github.com/nvim-lua/kickstart.nvim.git "%localappdata%\nvim" -``` - -If you're using `powershell.exe` - +nvim --headless "+qa" +nvim --headless "+checkhealth" "+qa" +luac -p init.lua lua/custom/**/*.lua ``` -git clone https://github.com/nvim-lua/kickstart.nvim.git "${env:LOCALAPPDATA}\nvim" -``` - -
-### Post Installation - -Start Neovim +Useful maintenance commands: ```sh -nvim +nvim --headless "+Lazy! sync" "+qa" +nvim --headless "+MasonToolsInstallSync" "+qa" ``` -That's it! Lazy will install all the plugins you have. Use `:Lazy` to view -the current plugin status. Hit `q` to close the window. - -#### Read The Friendly Documentation - -Read through the `init.lua` file in your configuration folder for more -information about extending and exploring Neovim. That also includes -examples of adding popularly requested plugins. - -> [!NOTE] -> For more information about a particular plugin check its repository's documentation. - - -### Getting Started - -[The Only Video You Need to Get Started with Neovim](https://youtu.be/m8C0Cq9Uv9o) - -### FAQ - -* What should I do if I already have a pre-existing Neovim configuration? - * You should back it up and then delete all associated files. - * This includes your existing init.lua and the Neovim files in `~/.local` - which can be deleted with `rm -rf ~/.local/share/nvim/` -* Can I keep my existing configuration in parallel to kickstart? - * Yes! You can use [NVIM_APPNAME](https://neovim.io/doc/user/starting.html#%24NVIM_APPNAME)`=nvim-NAME` - to maintain multiple configurations. For example, you can install the kickstart - configuration in `~/.config/nvim-kickstart` and create an alias: - ``` - alias nvim-kickstart='NVIM_APPNAME="nvim-kickstart" nvim' - ``` - When you run Neovim using `nvim-kickstart` alias it will use the alternative - config directory and the matching local directory - `~/.local/share/nvim-kickstart`. You can apply this approach to any Neovim - distribution that you would like to try out. -* What if I want to "uninstall" this configuration: - * See [lazy.nvim uninstall](https://lazy.folke.io/usage#-uninstalling) information -* Why is the kickstart `init.lua` a single file? Wouldn't it make sense to split it into multiple files? - * The main purpose of kickstart is to serve as a teaching tool and a reference - configuration that someone can easily use to `git clone` as a basis for their own. - As you progress in learning Neovim and Lua, you might consider splitting `init.lua` - into smaller parts. A fork of kickstart that does this while maintaining the - same functionality is available here: - * [kickstart-modular.nvim](https://github.com/dam9000/kickstart-modular.nvim) - * Discussions on this topic can be found here: - * [Restructure the configuration](https://github.com/nvim-lua/kickstart.nvim/issues/218) - * [Reorganize init.lua into a multi-file setup](https://github.com/nvim-lua/kickstart.nvim/pull/473) - -### Install Recipes - -Below you can find OS specific install instructions for Neovim and dependencies. - +## Keymap manual + +### Git workflow + +- `Gg`: open Neogit UI +- `Gd`: open Diffview +- `GD`: close Diffview +- `Gf`: Diffview file history (current file) +- `GF`: Diffview repo history +- `h...`: Gitsigns hunk actions (`:which-key h`) + +### Diagnostics and code navigation + +- `gd`: goto definition +- `gD`: goto declaration +- `gr`: goto references +- `gi`: goto implementation +- `gt`: goto type definition +- `xx`: Trouble diagnostics +- `xw`: Trouble workspace diagnostics +- `xd`: Trouble current buffer diagnostics +- `xq`: Trouble quickfix list +- `xl`: Trouble location list +- `tc`: toggle treesitter context header +- `jm` / `jk`: next/previous function start +- `jM` / `jK`: next/previous function end +- `jc` / `jC`: next/previous class start +- Textobject select (operator-pending/visual): `af`/`if` for function, `ac`/`ic` for class + +### Yank references (AI-friendly) + +- `ya`: yank absolute reference with location and symbol +- `yr`: yank relative reference with location and symbol +- `yf`: yank current buffer file as `@relative/path` +- `yd`: yank current buffer parent directory as `@relative/dir` + +### Tests and debug + +- `nr`: neotest run nearest +- `nf`: neotest run current file +- `ns`: neotest run suite (cwd) +- `nd`: neotest debug nearest via DAP +- `nn`: neotest summary toggle +- `no`: neotest output for nearest test +- `nO`: neotest output panel toggle +- `na`: attach to running neotest process +- `nS`: stop neotest run + +Existing DAP keys are unchanged: + +- `` continue/start, `` step into, `` step over, `` step out +- `` toggle dap-ui, `b` toggle breakpoint, `B` conditional bp + +### Search, replace, and explorer After installing all the dependencies continue with the [Install Kickstart](#install-kickstart) step. -#### Windows Installation +- `sR`: project search/replace with grug-far +- `eo`: open Oil explorer view (optional, non-default explorer) -
Windows with Microsoft C++ Build Tools and CMake -Installation may require installing build tools and updating the run command for `telescope-fzf-native` +### CMake workflow (optional) -See `telescope-fzf-native` documentation for [more details](https://github.com/nvim-telescope/telescope-fzf-native.nvim#installation) +- `cg`: CMake generate +- `cb`: CMake build +- `cr`: CMake run +- `ct`: CMake test +- `cc`: CMake select build type -This requires: +## Plugin stack by workflow -- Install CMake and the Microsoft C++ Build Tools on Windows +### LSP and language intelligence -```lua -{'nvim-telescope/telescope-fzf-native.nvim', build = 'cmake -S. -Bbuild -DCMAKE_BUILD_TYPE=Release && cmake --build build --config Release && cmake --install build --prefix build' } -``` -
-
Windows with gcc/make using chocolatey -Alternatively, one can install gcc and make which don't require changing the config, -the easiest way is to use choco: +- `nvim-lspconfig` + `mason-lspconfig` + `mason-tool-installer` +- Vue integration: + - `vue_ls` enabled + - `ts_ls` scoped to `vue` with `@vue/typescript-plugin` + - keeps `typescript-tools.nvim` available for TS/JS workflows +- Kubernetes/Helm integration: + - `yamlls` with schema mappings for Kubernetes, Helm chart, Helmfile, + and Kustomize + - `helm_ls` enabled + - `vim-helm` added for Helm syntax support -1. install [chocolatey](https://chocolatey.org/install) -either follow the instructions on the page or use winget, -run in cmd as **admin**: -``` -winget install --accept-source-agreements chocolatey.chocolatey -``` +### Treesitter and structural editing + +- `nvim-treesitter` uses current API (`require('nvim-treesitter').setup()`). +- `nvim-treesitter-context` provides sticky scope context. +- `nvim-treesitter-textobjects` adds structure-aware function/class jumps. +### Debugging 2. install all requirements using choco, exit the previous cmd and open a new one so that choco path is set, and run in cmd as **admin**: ``` @@ -207,9 +214,18 @@ sudo apt install make gcc ripgrep fd-find tree-sitter-cli unzip git xclip neovim ```
-#### Linux Install -
Ubuntu Install Steps +- Core: `nvim-dap`, `nvim-dap-ui`, `mason-nvim-dap`, `nvim-dap-go`. +- JS/TS: `nvim-dap-vscode-js` configured with `js-debug-adapter` and + `pwa-node` launch/attach defaults. +- C/C++: `codelldb` installation via Mason and baseline launch profile + (`Launch current file (codelldb)`). + +### Testing +- `neotest` core with adapters: + - `neotest-jest` + - `neotest-vitest` + - `neotest-gtest` ``` sudo add-apt-repository ppa:neovim-ppa/unstable -y sudo apt update @@ -222,26 +238,88 @@ sudo apt install make gcc ripgrep fd-find tree-sitter-cli unzip git xclip neovim sudo apt update sudo apt install make gcc ripgrep fd-find tree-sitter-cli unzip git xclip curl -# Now we install nvim -curl -LO https://github.com/neovim/neovim/releases/latest/download/nvim-linux-x86_64.tar.gz -sudo rm -rf /opt/nvim-linux-x86_64 -sudo mkdir -p /opt/nvim-linux-x86_64 -sudo chmod a+rX /opt/nvim-linux-x86_64 -sudo tar -C /opt -xzf nvim-linux-x86_64.tar.gz +Notes for C++ tests: -# make it available in /usr/local/bin, distro installs to /usr/bin -sudo ln -sf /opt/nvim-linux-x86_64/bin/nvim /usr/local/bin/ -``` -
-
Fedora Install Steps +- `neotest-gtest` needs executable mapping per project (use `:ConfigureGtest` + from the neotest summary window). +### Formatting and linting ``` sudo dnf install -y gcc make git ripgrep fd-find tree-sitter-cli unzip neovim ```
-
Arch Install Steps +- Formatting via `conform.nvim`: + - JS/TS/JSON/YAML: `prettierd` -> `prettier` + - C/C++: `clang_format` + - Lua: `stylua` +- Linting via `nvim-lint`: + - markdown: `markdownlint` + - dockerfile: `hadolint` + - yaml / yaml.helm-values: `yamllint` + +Linting is executable-aware for configured linters to avoid noisy diagnostics +when a linter binary is unavailable. + +### Project workflow plugins + +- `trouble.nvim`: focused diagnostics/issues panel +- `grug-far.nvim`: project-wide search/replace +- `oil.nvim`: optional file editing explorer (does not replace default explorer) +- `cmake-tools.nvim`: CMake build/run/test helpers (lazy and optional) + +## Mason-managed tools and servers + +This config ensures installation for key tools used by the workflows above, +including: + +- `prettierd`, `prettier`, `clang-format` +- `hadolint`, `yamllint`, `markdownlint`, `stylua` +- `js-debug-adapter`, `codelldb` +- configured LSP servers from `servers` table (including `helm_ls`) + +Check with `:Mason` and install manually if needed. + +## Typical workflows + +### JS/TS service workflow + +1. Edit with LSP + treesitter context. +2. Run nearest test with `nr` or file with `nf`. +3. Debug test or code path with `nd` / ``. +4. Use `sR` for safe project refactors. + +### C/C++ workflow + +1. Navigate symbols with `jm/jk/jc/jC`. +2. Build/test with CMake mappings if project uses CMake. +3. Debug using existing DAP keys and select `Launch current file (codelldb)`. +4. Run gtest via neotest after `:ConfigureGtest` setup. + +### Kubernetes/Helm workflow + +1. Edit manifests with `yamlls` schema-backed completion/validation. +2. Edit charts/templates with Helm support (`helm_ls`, `vim-helm`). +3. Use `sR` for scoped multi-file YAML refactors. + +## Troubleshooting + +- Verify startup: `nvim --headless "+qa"` +- Verify health: `nvim --headless "+checkhealth" "+qa"` +- Verify LSP clients in current buffer: `:LspInfo` +- Verify formatter mapping: `:ConformInfo` +- Verify Mason state: `:Mason` +- Re-sync plugins: `:Lazy sync` + +If a new feature appears missing, first confirm lazy-loading trigger +(keymap/filetype/command) was actually used. + +## Help docs + +This repo ships a Vim help file: +- `:help nvim-config` +- `:help nvimn-config` ``` sudo pacman -S --noconfirm --needed gcc make git ripgrep fd tree-sitter-cli unzip neovim ``` diff --git a/doc/nvim.txt b/doc/nvim.txt new file mode 100644 index 00000000000..9efabeb6ef3 --- /dev/null +++ b/doc/nvim.txt @@ -0,0 +1,50 @@ +============================================================================== +NVIM CONFIG *nvim-config* + *nvimn-config* + +This is a custom Neovim configuration based on kickstart.nvim. + +QUICK CHECKS *nvim-config-quick-checks* + +Run these commands after config changes: + +> + nvim --headless "+qa" + nvim --headless "+checkhealth" "+qa" + luac -p init.lua lua/custom/**/*.lua +< + +GIT KEYMAPS *nvim-config-git* + +Core git mappings in normal mode: + +- gg: open Neogit +- gd: open Diffview +- gD: close Diffview +- gf: Diffview file history for current file +- gF: Diffview repository history +- h...: Gitsigns hunk actions + +Use |which-key| with g and h to discover more actions. + +LSP: VUE + TYPESCRIPT *nvim-config-vue* + +Vue buffers rely on both: + +- `vue_ls` for Vue language features +- `ts_ls` (filetype-scoped to `vue`) with `@vue/typescript-plugin` + +This pairing allows `vue_ls` to forward TypeScript requests for `.vue` files. + +TREESITTER *nvim-config-treesitter* + +This config uses the current nvim-treesitter API: + +> + require('nvim-treesitter').setup() +< + +Configured parsers are installed from `ensure_installed` as needed. + +============================================================================== + vim:tw=78:ts=8:ft=help:norl: diff --git a/doc/tags b/doc/tags index 687ae7721d9..857a702e9a4 100644 --- a/doc/tags +++ b/doc/tags @@ -1,3 +1,9 @@ kickstart-is kickstart.txt /*kickstart-is* kickstart-is-not kickstart.txt /*kickstart-is-not* kickstart.nvim kickstart.txt /*kickstart.nvim* +nvim-config nvim.txt /*nvim-config* +nvim-config-git nvim.txt /*nvim-config-git* +nvim-config-quick-checks nvim.txt /*nvim-config-quick-checks* +nvim-config-treesitter nvim.txt /*nvim-config-treesitter* +nvim-config-vue nvim.txt /*nvim-config-vue* +nvimn-config nvim.txt /*nvimn-config* diff --git a/init.lua b/init.lua index cacc5b7b1c3..d1fb928a97c 100644 --- a/init.lua +++ b/init.lua @@ -1,108 +1,21 @@ ---[[ - -===================================================================== -==================== READ THIS BEFORE CONTINUING ==================== -===================================================================== -======== .-----. ======== -======== .----------------------. | === | ======== -======== |.-""""""""""""""""""-.| |-----| ======== -======== || || | === | ======== -======== || KICKSTART.NVIM || |-----| ======== -======== || || | === | ======== -======== || || |-----| ======== -======== ||:Tutor || |:::::| ======== -======== |'-..................-'| |____o| ======== -======== `"")----------------(""` ___________ ======== -======== /::::::::::| |::::::::::\ \ no mouse \ ======== -======== /:::========| |==hjkl==:::\ \ required \ ======== -======== '""""""""""""' '""""""""""""' '""""""""""' ======== -======== ======== -===================================================================== -===================================================================== - -What is Kickstart? - - Kickstart.nvim is *not* a distribution. - - Kickstart.nvim is a starting point for your own configuration. - The goal is that you can read every line of code, top-to-bottom, understand - what your configuration is doing, and modify it to suit your needs. - - Once you've done that, you can start exploring, configuring and tinkering to - make Neovim your own! That might mean leaving Kickstart just the way it is for a while - or immediately breaking it into modular pieces. It's up to you! - - If you don't know anything about Lua, I recommend taking some time to read through - a guide. One possible example which will only take 10-15 minutes: - - https://learnxinyminutes.com/docs/lua/ - - After understanding a bit more about Lua, you can use `:help lua-guide` as a - reference for how Neovim integrates Lua. - - :help lua-guide - - (or HTML version): https://neovim.io/doc/user/lua-guide.html - -Kickstart Guide: - - TODO: The very first thing you should do is to run the command `:Tutor` in Neovim. - - If you don't know what this means, type the following: - - - - : - - Tutor - - - - (If you already know the Neovim basics, you can skip this step.) - - Once you've completed that, you can continue working through **AND READING** the rest - of the kickstart init.lua. - - Next, run AND READ `:help`. - This will open up a help window with some basic information - about reading, navigating and searching the builtin help documentation. - - This should be the first place you go to look when you're stuck or confused - with something. It's one of my favorite Neovim features. - - MOST IMPORTANTLY, we provide a keymap "sh" to [s]earch the [h]elp documentation, - which is very useful when you're not exactly sure of what you're looking for. - - I have left several `:help X` comments throughout the init.lua - These are hints about where to find more information about the relevant settings, - plugins or Neovim features used in Kickstart. - - NOTE: Look for lines like this - - Throughout the file. These are for you, the reader, to help you understand what is happening. - Feel free to delete them once you know what you're doing, but they should serve as a guide - for when you are first encountering a few different constructs in your Neovim config. - -If you experience any errors while trying to install kickstart, run `:checkhealth` for more info. - -I hope you enjoy your Neovim journey, -- TJ - -P.S. You can delete this when you're done too. It's your config now! :) ---]] +-- Prepend mise shims to PATH so Mason/LSP can find node, go, etc. +vim.env.PATH = vim.env.HOME .. '/.local/share/mise/shims:' .. vim.env.PATH -- Set as the leader key -- See `:help mapleader` --- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used) +-- Must happen before plugins are loaded. vim.g.mapleader = ' ' vim.g.maplocalleader = ' ' -- Set to true if you have a Nerd Font installed and selected in the terminal -vim.g.have_nerd_font = false +vim.g.have_nerd_font = true -- [[ Setting options ]] -- See `:help vim.o` --- NOTE: You can change these options as you wish! --- For more options, you can see `:help option-list` -- Make line numbers default vim.o.number = true --- You can also add relative line numbers, to help with jumping. --- Experiment for yourself to see if you like it! --- vim.o.relativenumber = true +vim.o.relativenumber = true -- Enable mouse mode, can be useful for resizing splits for example! vim.o.mouse = 'a' @@ -116,8 +29,31 @@ vim.o.showmode = false -- See `:help 'clipboard'` vim.schedule(function() vim.o.clipboard = 'unnamedplus' end) --- Enable break indent -vim.o.breakindent = true +-- Default to 2-space indentation unless overridden by filetype/plugins +vim.o.tabstop = 2 +vim.o.shiftwidth = 2 +vim.o.softtabstop = 2 +vim.o.expandtab = true + +vim.filetype.add { + extension = { + mdx = 'mdx', + }, + filename = { + ['compose.yaml'] = 'yaml.docker-compose', + ['compose.yml'] = 'yaml.docker-compose', + ['docker-compose.yaml'] = 'yaml.docker-compose', + ['docker-compose.yml'] = 'yaml.docker-compose', + ['.gitlab-ci.yaml'] = 'yaml.gitlab', + ['.gitlab-ci.yml'] = 'yaml.gitlab', + }, + pattern = { + ['.*/templates/.*%.yaml'] = 'helm', + ['.*/templates/.*%.yml'] = 'helm', + ['.*/values.*%.yaml'] = 'yaml.helm-values', + ['.*/values.*%.yml'] = 'yaml.helm-values', + }, +} -- Enable undo/redo changes even after closing and reopening a file vim.o.undofile = true @@ -164,6 +100,14 @@ vim.o.scrolloff = 10 -- See `:help 'confirm'` vim.o.confirm = true +-- Modern UI defaults and safer project-local behavior. +vim.o.winborder = 'rounded' +vim.o.pumborder = 'rounded' +vim.o.pummaxwidth = 80 +vim.o.smoothscroll = true +vim.o.modeline = false +vim.o.jumpoptions = 'clean,view' + -- [[ Basic Keymaps ]] -- See `:help vim.keymap.set()` @@ -173,21 +117,61 @@ vim.keymap.set('n', '', 'nohlsearch') -- Diagnostic Config & Keymaps -- See :help vim.diagnostic.Opts +local function diagnostic_on_jump(diagnostic) + if diagnostic then vim.diagnostic.open_float { scope = 'cursor', focus = false } end +end + vim.diagnostic.config { update_in_insert = false, severity_sort = true, float = { border = 'rounded', source = 'if_many' }, - underline = { severity = { min = vim.diagnostic.severity.WARN } }, - - -- Can switch between these as you prefer - virtual_text = true, -- Text shows up at the end of the line - virtual_lines = false, -- Text shows up underneath the line, with virtual lines - - -- Auto open the float, so you can easily read the errors when jumping with `[d` and `]d` - jump = { float = true }, + underline = { severity = vim.diagnostic.severity.ERROR }, + signs = vim.g.have_nerd_font and { + text = { + [vim.diagnostic.severity.ERROR] = '󰅚 ', + [vim.diagnostic.severity.WARN] = '󰀪 ', + [vim.diagnostic.severity.INFO] = '󰋽 ', + [vim.diagnostic.severity.HINT] = '󰌶 ', + }, + } or {}, + virtual_text = { + source = 'if_many', + spacing = 2, + severity = { min = vim.diagnostic.severity.WARN }, + format = function(diagnostic) + if diagnostic.severity == vim.diagnostic.severity.WARN then return 'W: ' .. diagnostic.message end + if diagnostic.severity == vim.diagnostic.severity.ERROR then return 'E: ' .. diagnostic.message end + return nil + end, + }, + virtual_lines = false, + jump = { on_jump = diagnostic_on_jump }, } vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' }) +vim.keymap.set('n', '[d', function() vim.diagnostic.jump { count = -1 } end, { desc = 'Go to previous [D]iagnostic' }) +vim.keymap.set('n', ']d', function() vim.diagnostic.jump { count = 1 } end, { desc = 'Go to next [D]iagnostic' }) +vim.keymap.set('n', 'de', vim.diagnostic.open_float, { desc = 'Show [D]iagnostic [E]rror details' }) +vim.keymap.set('n', 'dy', function() + local line = vim.api.nvim_win_get_cursor(0)[1] + local diagnostics = vim.diagnostic.get(0, { lnum = line - 1 }) + if #diagnostics == 0 then + vim.notify('No diagnostics on this line', vim.log.levels.INFO) + return + end + local file_path = vim.api.nvim_buf_get_name(0) + local root = vim.fs.root(file_path, { '.git' }) or vim.fn.getcwd() + local prefix = root:match '/$' and root or (root .. '/') + local rel = file_path:sub(1, #prefix) == prefix and file_path:sub(#prefix + 1) or file_path + local parts = {} + for _, d in ipairs(diagnostics) do + table.insert(parts, string.format('@%s:%d: %s', rel, d.lnum + 1, d.message)) + end + local result = table.concat(parts, '\n') + vim.fn.setreg('"', result) + pcall(vim.fn.setreg, '+', result) + vim.notify('Yanked ' .. #diagnostics .. ' diagnostic(s)', vim.log.levels.INFO) +end, { desc = '[D]iagnostic [Y]ank to clipboard' }) -- Exit terminal mode in the builtin terminal with a shortcut that is a bit easier -- for people to discover. Otherwise, you normally need to press , which @@ -204,13 +188,13 @@ vim.keymap.set('t', '', '', { desc = 'Exit terminal mode' } -- vim.keymap.set('n', '', 'echo "Use j to move!!"') -- Keybinds to make split navigation easier. --- Use CTRL+ to switch between windows +-- NOTE: are managed by vim-tmux-navigator in this config. -- -- See `:help wincmd` for a list of all window commands -vim.keymap.set('n', '', '', { desc = 'Move focus to the left window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the right window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the lower window' }) -vim.keymap.set('n', '', '', { desc = 'Move focus to the upper window' }) +vim.keymap.set('n', 'wh', '', { desc = 'Move focus to the left window' }) +vim.keymap.set('n', 'wl', '', { desc = 'Move focus to the right window' }) +vim.keymap.set('n', 'wj', '', { desc = 'Move focus to the lower window' }) +vim.keymap.set('n', 'wk', '', { desc = 'Move focus to the upper window' }) -- NOTE: Some terminals have colliding keymaps or are not able to send distinct keycodes -- vim.keymap.set("n", "", "H", { desc = "Move window to the left" }) @@ -230,6 +214,48 @@ vim.api.nvim_create_autocmd('TextYankPost', { callback = function() vim.hl.on_yank() end, }) +local path_on_save_group = vim.api.nvim_create_augroup('kickstart-create-path-on-save', { clear = true }) + +-- Only create paths inside these roots (defaults to current working directory). +-- Add more roots as needed, e.g. vim.fn.expand '~/notes' +local path_create_roots = { + vim.fn.getcwd(), +} + +local is_in_allowed_root = function(path) + local abs_path = vim.fn.fnamemodify(path, ':p') + for _, root in ipairs(path_create_roots) do + local abs_root = vim.fn.fnamemodify(root, ':p') + if abs_path:sub(1, #abs_root) == abs_root then return true end + end + return false +end + +local ensure_owner_rwx = function(path) + local perms = vim.fn.getfperm(path) + if perms ~= '' and perms:sub(1, 3) ~= 'rwx' then vim.fn.setfperm(path, 'rwx' .. perms:sub(4)) end +end + +vim.api.nvim_create_autocmd('BufWritePre', { + desc = 'Create missing parent directories on save', + group = path_on_save_group, + callback = function(args) + if vim.bo[args.buf].buftype ~= '' then return end + + local file_path = vim.api.nvim_buf_get_name(args.buf) + if file_path == '' or file_path:match '^%w+://' then return end + + local uv = vim.uv or vim.loop + local abs_path = vim.fn.fnamemodify(file_path, ':p') + if not is_in_allowed_root(abs_path) then return end + + local parent = vim.fn.fnamemodify(abs_path, ':h') + + if uv.fs_stat(parent) == nil then vim.fn.mkdir(parent, 'p', '0700') end + ensure_owner_rwx(parent) + end, +}) + -- [[ Install `lazy.nvim` plugin manager ]] -- See `:help lazy.nvim.txt` or https://github.com/folke/lazy.nvim for more info local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim' @@ -243,167 +269,78 @@ end local rtp = vim.opt.rtp rtp:prepend(lazypath) +local treesitter_parsers = { + 'bash', + 'c', + 'diff', + 'html', + 'lua', + 'luadoc', + 'markdown', + 'markdown_inline', + 'query', + 'vim', + 'vimdoc', +} + -- [[ Configure and install plugins ]] --- --- To check the current status of your plugins, run --- :Lazy --- --- You can press `?` in this menu for help. Use `:q` to close the window --- --- To update plugins you can run --- :Lazy update --- --- NOTE: Here is where you install your plugins. require('lazy').setup({ - -- NOTE: Plugins can be added via a link or github org/name. To run setup automatically, use `opts = {}` { 'NMAC427/guess-indent.nvim', opts = {} }, - -- Alternatively, use `config = function() ... end` for full control over the configuration. - -- If you prefer to call `setup` explicitly, use: - -- { - -- 'lewis6991/gitsigns.nvim', - -- config = function() - -- require('gitsigns').setup({ - -- -- Your gitsigns configuration here - -- }) - -- end, - -- } - -- - -- Here is a more advanced example where we pass configuration - -- options to `gitsigns.nvim`. - -- - -- See `:help gitsigns` to understand what the configuration keys do - { -- Adds git related signs to the gutter, as well as utilities for managing changes - 'lewis6991/gitsigns.nvim', - ---@module 'gitsigns' - ---@type Gitsigns.Config - ---@diagnostic disable-next-line: missing-fields - opts = { - signs = { - add = { text = '+' }, ---@diagnostic disable-line: missing-fields - change = { text = '~' }, ---@diagnostic disable-line: missing-fields - delete = { text = '_' }, ---@diagnostic disable-line: missing-fields - topdelete = { text = '‾' }, ---@diagnostic disable-line: missing-fields - changedelete = { text = '~' }, ---@diagnostic disable-line: missing-fields - }, - }, - }, - - -- NOTE: Plugins can also be configured to run Lua code when they are loaded. - -- - -- This is often very useful to both group configuration, as well as handle - -- lazy loading plugins that don't need to be loaded immediately at startup. - -- - -- For example, in the following configuration, we use: - -- event = 'VimEnter' - -- - -- which loads which-key before all the UI elements are loaded. Events can be - -- normal autocommands events (`:help autocmd-events`). - -- - -- Then, because we use the `opts` key (recommended), the configuration runs - -- after the plugin has been loaded as `require(MODULE).setup(opts)`. - - { -- Useful plugin to show you pending keybinds. + { 'folke/which-key.nvim', event = 'VimEnter', ---@module 'which-key' ---@type wk.Opts ---@diagnostic disable-next-line: missing-fields opts = { - -- delay between pressing a key and opening which-key (milliseconds) delay = 0, icons = { mappings = vim.g.have_nerd_font }, - -- Document existing key chains spec = { - { 's', group = '[S]earch', mode = { 'n', 'v' } }, + { 'a', group = 'Harpoon [A]dd' }, + { 'c', group = '[C]Make' }, + { 'd', group = '[D]iagnostics' }, + { 'e', group = '[E]xplorer' }, + { 'g', group = '[G]oto' }, + { 'G', group = '[G]it' }, + { 'm', group = '[M]arkdown' }, + { 'n', group = '[N]eotest' }, + { 's', group = '[S]earch' }, { 't', group = '[T]oggle' }, - { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, -- Enable gitsigns recommended keymaps first + { 'w', group = '[W]indow' }, + { 'x', group = 'Trouble' }, + { 'j', group = '[J]ump' }, + { 'h', group = 'Git [H]unk', mode = { 'n', 'v' } }, { 'gr', group = 'LSP Actions', mode = { 'n' } }, }, }, }, - -- NOTE: Plugins can specify dependencies. - -- - -- The dependencies are proper plugin specifications as well - anything - -- you do for a plugin at the top level, you can do for a dependency. - -- - -- Use the `dependencies` key to specify the dependencies of a particular plugin - { -- Fuzzy Finder (files, lsp, etc) 'nvim-telescope/telescope.nvim', - -- By default, Telescope is included and acts as your picker for everything. - - -- If you would like to switch to a different picker (like snacks, or fzf-lua) - -- you can disable the Telescope plugin by setting enabled to false and enable - -- your replacement picker by requiring it explicitly (e.g. 'custom.plugins.snacks') - - -- Note: If you customize your config for yourself, - -- it’s best to remove the Telescope plugin config entirely - -- instead of just disabling it here, to keep your config clean. enabled = true, event = 'VimEnter', dependencies = { 'nvim-lua/plenary.nvim', - { -- If encountering errors, see telescope-fzf-native README for installation instructions + { 'nvim-telescope/telescope-fzf-native.nvim', - - -- `build` is used to run some command when the plugin is installed/updated. - -- This is only run then, not every time Neovim starts up. build = 'make', - - -- `cond` is a condition used to determine whether this plugin should be - -- installed and loaded. cond = function() return vim.fn.executable 'make' == 1 end, }, { 'nvim-telescope/telescope-ui-select.nvim' }, - - -- Useful for getting pretty icons, but requires a Nerd Font. { 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font }, }, config = function() - -- Telescope is a fuzzy finder that comes with a lot of different things that - -- it can fuzzy find! It's more than just a "file finder", it can search - -- many different aspects of Neovim, your workspace, LSP, and more! - -- - -- The easiest way to use Telescope, is to start by doing something like: - -- :Telescope help_tags - -- - -- After running this command, a window will open up and you're able to - -- type in the prompt window. You'll see a list of `help_tags` options and - -- a corresponding preview of the help. - -- - -- Two important keymaps to use while in Telescope are: - -- - Insert mode: - -- - Normal mode: ? - -- - -- This opens a window that shows you all of the keymaps for the current - -- Telescope picker. This is really useful to discover what Telescope can - -- do as well as how to actually do it! - - -- [[ Configure Telescope ]] - -- See `:help telescope` and `:help telescope.setup()` require('telescope').setup { - -- You can put your default mappings / updates / etc. in here - -- All the info you're looking for is in `:help telescope.setup()` - -- - -- defaults = { - -- mappings = { - -- i = { [''] = 'to_fuzzy_refine' }, - -- }, - -- }, - -- pickers = {} extensions = { ['ui-select'] = { require('telescope.themes').get_dropdown() }, }, } - -- Enable Telescope extensions if they are installed pcall(require('telescope').load_extension, 'fzf') pcall(require('telescope').load_extension, 'ui-select') - -- See `:help telescope.builtin` local builtin = require 'telescope.builtin' vim.keymap.set('n', 'sh', builtin.help_tags, { desc = '[S]earch [H]elp' }) vim.keymap.set('n', 'sk', builtin.keymaps, { desc = '[S]earch [K]eymaps' }) @@ -417,51 +354,18 @@ require('lazy').setup({ vim.keymap.set('n', 'sc', builtin.commands, { desc = '[S]earch [C]ommands' }) vim.keymap.set('n', '', builtin.buffers, { desc = '[ ] Find existing buffers' }) - -- This runs on LSP attach per buffer (see main LSP attach function in 'neovim/nvim-lspconfig' config for more info, - -- it is better explained there). This allows easily switching between pickers if you prefer using something else! - vim.api.nvim_create_autocmd('LspAttach', { - group = vim.api.nvim_create_augroup('telescope-lsp-attach', { clear = true }), - callback = function(event) - local buf = event.buf - - -- Find references for the word under your cursor. - vim.keymap.set('n', 'grr', builtin.lsp_references, { buffer = buf, desc = '[G]oto [R]eferences' }) - - -- Jump to the implementation of the word under your cursor. - -- Useful when your language has ways of declaring types without an actual implementation. - vim.keymap.set('n', 'gri', builtin.lsp_implementations, { buffer = buf, desc = '[G]oto [I]mplementation' }) - - -- Jump to the definition of the word under your cursor. - -- This is where a variable was first declared, or where a function is defined, etc. - -- To jump back, press . - vim.keymap.set('n', 'grd', builtin.lsp_definitions, { buffer = buf, desc = '[G]oto [D]efinition' }) - - -- Fuzzy find all the symbols in your current document. - -- Symbols are things like variables, functions, types, etc. - vim.keymap.set('n', 'gO', builtin.lsp_document_symbols, { buffer = buf, desc = 'Open Document Symbols' }) - - -- Fuzzy find all the symbols in your current workspace. - -- Similar to document symbols, except searches over your entire project. - vim.keymap.set('n', 'gW', builtin.lsp_dynamic_workspace_symbols, { buffer = buf, desc = 'Open Workspace Symbols' }) - - -- Jump to the type of the word under your cursor. - -- Useful when you're not sure what type a variable is and you want to see - -- the definition of its *type*, not where it was *defined*. - vim.keymap.set('n', 'grt', builtin.lsp_type_definitions, { buffer = buf, desc = '[G]oto [T]ype Definition' }) + vim.keymap.set( + 'n', + '/', + function() + builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { + winblend = 10, + previewer = false, + }) end, - }) + { desc = '[/] Fuzzily search in current buffer' } + ) - -- Override default behavior and theme when searching - vim.keymap.set('n', '/', function() - -- You can pass additional configuration to Telescope to change the theme, layout, etc. - builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown { - winblend = 10, - previewer = false, - }) - end, { desc = '[/] Fuzzily search in current buffer' }) - - -- It's also possible to pass additional configuration options. - -- See `:help telescope.builtin.live_grep()` for information about particular keys vim.keymap.set( 'n', 's/', @@ -474,19 +378,13 @@ require('lazy').setup({ { desc = '[S]earch [/] in Open Files' } ) - -- Shortcut for searching your Neovim configuration files vim.keymap.set('n', 'sn', function() builtin.find_files { cwd = vim.fn.stdpath 'config' } end, { desc = '[S]earch [N]eovim files' }) end, }, - -- LSP Plugins { - -- Main LSP Configuration 'neovim/nvim-lspconfig', dependencies = { - -- Automatically install LSPs and related tools to stdpath for Neovim - -- Mason must be loaded before its dependents so we need to set it up here. - -- NOTE: `opts = {}` is the same as calling `require('mason').setup({})` { 'mason-org/mason.nvim', ---@module 'mason.settings' @@ -494,74 +392,37 @@ require('lazy').setup({ ---@diagnostic disable-next-line: missing-fields opts = {}, }, - -- Maps LSP server names between nvim-lspconfig and Mason package names. 'mason-org/mason-lspconfig.nvim', 'WhoIsSethDaniel/mason-tool-installer.nvim', - - -- Useful status updates for LSP. { 'j-hui/fidget.nvim', opts = {} }, }, config = function() - -- Brief aside: **What is LSP?** - -- - -- LSP is an initialism you've probably heard, but might not understand what it is. - -- - -- LSP stands for Language Server Protocol. It's a protocol that helps editors - -- and language tooling communicate in a standardized fashion. - -- - -- In general, you have a "server" which is some tool built to understand a particular - -- language (such as `gopls`, `lua_ls`, `rust_analyzer`, etc.). These Language Servers - -- (sometimes called LSP servers, but that's kind of like ATM Machine) are standalone - -- processes that communicate with some "client" - in this case, Neovim! - -- - -- LSP provides Neovim with features like: - -- - Go to definition - -- - Find references - -- - Autocompletion - -- - Symbol Search - -- - and more! - -- - -- Thus, Language Servers are external tools that must be installed separately from - -- Neovim. This is where `mason` and related plugins come into play. - -- - -- If you're wondering about lsp vs treesitter, you can check out the wonderfully - -- and elegantly composed help section, `:help lsp-vs-treesitter` - - -- This function gets run when an LSP attaches to a particular buffer. - -- That is to say, every time a new file is opened that is associated with - -- an lsp (for example, opening `main.rs` is associated with `rust_analyzer`) this - -- function will be executed to configure the current buffer vim.api.nvim_create_autocmd('LspAttach', { group = vim.api.nvim_create_augroup('kickstart-lsp-attach', { clear = true }), callback = function(event) - -- NOTE: Remember that Lua is a real programming language, and as such it is possible - -- to define small helper and utility functions so you don't have to repeat yourself. - -- - -- In this case, 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 map = function(keys, func, desc, mode) mode = mode or 'n' vim.keymap.set(mode, keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc }) end - -- Rename the variable under your cursor. - -- Most Language Servers support renaming across files, etc. - map('grn', vim.lsp.buf.rename, '[R]e[n]ame') + local builtin = require 'telescope.builtin' + local client = vim.lsp.get_client_by_id(event.data.client_id) - -- Execute a code action, usually your cursor needs to be on top of an error - -- or a suggestion from your LSP for this to activate. + map('grn', vim.lsp.buf.rename, '[R]e[n]ame') map('gra', vim.lsp.buf.code_action, '[G]oto Code [A]ction', { 'n', 'x' }) - - -- WARN: This is not Goto Definition, this is Goto Declaration. - -- For example, in C this would take you to the header. + map('grr', builtin.lsp_references, '[G]oto [R]eferences') + map('gr', builtin.lsp_references, '[G]oto [R]eferences') + map('gri', builtin.lsp_implementations, '[G]oto [I]mplementation') + map('gi', builtin.lsp_implementations, '[G]oto [I]mplementation') + map('grd', builtin.lsp_definitions, '[G]oto [D]efinition') + map('gd', builtin.lsp_definitions, '[G]oto [D]efinition') map('grD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') + map('gD', vim.lsp.buf.declaration, '[G]oto [D]eclaration') + map('gO', builtin.lsp_document_symbols, 'Open Document Symbols') + map('gW', builtin.lsp_dynamic_workspace_symbols, 'Open Workspace Symbols') + map('grt', builtin.lsp_type_definitions, '[G]oto [T]ype Definition') + map('gt', builtin.lsp_type_definitions, '[G]oto [T]ype Definition') - -- The following two autocommands are used to highlight references of the - -- word under your cursor when your cursor rests there for a little while. - -- See `:help CursorHold` for information about when this is executed - -- - -- When you move your cursor, the highlights will be cleared (the second autocommand). - local client = vim.lsp.get_client_by_id(event.data.client_id) if client and client:supports_method('textDocument/documentHighlight', event.buf) then local highlight_augroup = vim.api.nvim_create_augroup('kickstart-lsp-highlight', { clear = false }) vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, { @@ -585,81 +446,160 @@ require('lazy').setup({ }) end - -- The following code creates a keymap to toggle inlay hints in your - -- code, if the language server you are using supports them - -- - -- This may be unwanted, since they displace some of your code if client and client:supports_method('textDocument/inlayHint', event.buf) then map('th', function() vim.lsp.inlay_hint.enable(not vim.lsp.inlay_hint.is_enabled { bufnr = event.buf }) end, '[T]oggle Inlay [H]ints') end + + if client and client:supports_method('textDocument/codeLens', event.buf) then vim.lsp.codelens.enable(true, { bufnr = event.buf }) end + + if client and client:supports_method('textDocument/linkedEditingRange', event.buf) then + vim.lsp.linked_editing_range.enable(true, { client_id = client.id }) + end end, }) - -- Enable the following language servers - -- Feel free to add/remove any LSPs that you want here. They will automatically be installed. - -- See `:help lsp-config` for information about keys and how to configure - ---@type table + local vue_language_server_path = vim.fn.stdpath 'data' .. '/mason/packages/vue-language-server/node_modules/@vue/language-server' + local vue_typescript_plugin = { + name = '@vue/typescript-plugin', + location = vue_language_server_path, + languages = { 'vue' }, + configNamespace = 'typescript', + } + local has_go = vim.fn.executable 'go' == 1 + local servers = { - -- clangd = {}, - -- gopls = {}, - -- pyright = {}, - -- rust_analyzer = {}, - -- - -- Some languages (like typescript) have entire language plugins that can be useful: - -- https://github.com/pmizio/typescript-tools.nvim - -- - -- But for many setups, the LSP (`ts_ls`) will work just fine - -- ts_ls = {}, - - stylua = {}, -- Used to format Lua code - - -- Special Lua Config, as recommended by neovim help docs + -- Languages + clangd = {}, + basedpyright = { + settings = { + basedpyright = { + analysis = { + typeCheckingMode = 'standard', + diagnosticMode = 'openFilesOnly', + }, + }, + }, + }, + ruff = {}, + rust_analyzer = {}, + bashls = {}, + awk_ls = {}, + cssls = {}, + html = {}, + jsonls = {}, + yamlls = { + settings = { + yaml = { + schemas = { + ['https://raw.githubusercontent.com/yannh/kubernetes-json-schema/refs/heads/master/v1.32.1-standalone-strict/all.json'] = { + '*.k8s.yaml', + 'k8s/**/*.yaml', + 'manifests/**/*.yaml', + 'kubernetes/**/*.yaml', + }, + ['https://json.schemastore.org/chart'] = 'Chart.yaml', + ['https://json.schemastore.org/helmfile.json'] = 'helmfile.yaml', + ['https://json.schemastore.org/kustomization.json'] = 'kustomization.yaml', + }, + }, + }, + }, + taplo = {}, + elixirls = {}, + gh_actions_ls = {}, lua_ls = { - on_init = function(client) - if client.workspace_folders then - local path = client.workspace_folders[1].name - if path ~= vim.fn.stdpath 'config' and (vim.uv.fs_stat(path .. '/.luarc.json') or vim.uv.fs_stat(path .. '/.luarc.jsonc')) then return end - end - - client.config.settings.Lua = vim.tbl_deep_extend('force', client.config.settings.Lua, { - runtime = { - version = 'LuaJIT', - path = { 'lua/?.lua', 'lua/?/init.lua' }, + settings = { + Lua = { + completion = { + callSnippet = 'Replace', + }, + diagnostics = { + globals = { 'vim' }, }, workspace = { checkThirdParty = false, - -- NOTE: this is a lot slower and will cause issues when working on your own configuration. - -- See https://github.com/neovim/nvim-lspconfig/issues/3189 - library = vim.tbl_extend('force', vim.api.nvim_get_runtime_file('', true), { - '${3rd}/luv/library', - '${3rd}/busted/library', - }), + library = vim.api.nvim_get_runtime_file('', true), }, - }) - end, - settings = { - Lua = {}, + }, }, }, + eslint = {}, + astro = {}, + vue_ls = {}, + helm_ls = {}, + ts_ls = { + filetypes = { 'vue' }, + init_options = { + plugins = { vue_typescript_plugin }, + }, + }, + tailwindcss = {}, + docker_language_server = {}, + docker_compose_language_service = {}, + marksman = {}, + postgres_lsp = {}, + neocmake = {}, + buf_ls = {}, } - -- Ensure the servers and tools above are installed - -- - -- To check the current status of installed tools and/or manually install - -- other tools, you can run - -- :Mason - -- - -- You can press `g?` for help in this menu. - local ensure_installed = vim.tbl_keys(servers or {}) + if has_go then + servers.gopls = { + settings = { + gopls = { + analyses = { + unusedparams = true, + shadow = true, + }, + staticcheck = true, + gofumpt = true, + }, + }, + } + end + + if vim.fn.executable 'jq-lsp' == 1 then servers.jqls = {} end + + servers.marksman.filetypes = { 'markdown', 'mdx' } + servers.tailwindcss.filetypes = { + 'astro', + 'css', + 'html', + 'javascript', + 'javascriptreact', + 'less', + 'markdown', + 'mdx', + 'sass', + 'scss', + 'svelte', + 'typescript', + 'typescriptreact', + 'vue', + } + + ---@type MasonLspconfigSettings + ---@diagnostic disable-next-line: missing-fields + require('mason-lspconfig').setup { + automatic_enable = vim.tbl_keys(servers or {}), + } + + local ensure_installed = vim.tbl_filter(function(server_name) return server_name ~= 'gopls' and server_name ~= 'jqls' end, vim.tbl_keys(servers or {})) vim.list_extend(ensure_installed, { - -- You can add other tools here that you want Mason to install + 'stylua', + 'markdownlint', + 'prettierd', + 'prettier', + 'clang-format', + 'hadolint', + 'yamllint', + 'js-debug-adapter', + 'codelldb', }) require('mason-tool-installer').setup { ensure_installed = ensure_installed } - for name, server in pairs(servers) do - vim.lsp.config(name, server) - vim.lsp.enable(name) + for server_name, config in pairs(servers) do + vim.lsp.config(server_name, config) end end, }, @@ -681,9 +621,6 @@ require('lazy').setup({ opts = { notify_on_error = false, format_on_save = function(bufnr) - -- Disable "format_on_save lsp_fallback" for languages that don't - -- have a well standardized coding style. You can add additional - -- languages here or re-enable it for the disabled ones. local disable_filetypes = { c = true, cpp = true } if disable_filetypes[vim.bo[bufnr].filetype] then return nil @@ -696,11 +633,15 @@ require('lazy').setup({ end, formatters_by_ft = { lua = { 'stylua' }, - -- Conform can also run multiple formatters sequentially - -- python = { "isort", "black" }, - -- - -- You can use 'stop_after_first' to run the first available formatter from the list - -- javascript = { "prettierd", "prettier", stop_after_first = true }, + 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 }, + json = { 'prettierd', 'prettier', stop_after_first = true }, + jsonc = { 'prettierd', 'prettier', stop_after_first = true }, + yaml = { 'prettierd', 'prettier', stop_after_first = true }, + c = { 'clang_format' }, + cpp = { 'clang_format' }, }, }, }, @@ -710,28 +651,13 @@ require('lazy').setup({ event = 'VimEnter', version = '1.*', dependencies = { - -- Snippet Engine { 'L3MON4D3/LuaSnip', version = '2.*', build = (function() - -- Build Step is needed for regex support in snippets. - -- This step is not supported in many windows environments. - -- Remove the below condition to re-enable on windows. if vim.fn.has 'win32' == 1 or vim.fn.executable 'make' == 0 then return end return 'make install_jsregexp' end)(), - dependencies = { - -- `friendly-snippets` contains a variety of premade snippets. - -- See the README about individual language/framework/plugin snippets: - -- https://github.com/rafamadriz/friendly-snippets - -- { - -- 'rafamadriz/friendly-snippets', - -- config = function() - -- require('luasnip.loaders.from_vscode').lazy_load() - -- end, - -- }, - }, opts = {}, }, }, @@ -739,42 +665,14 @@ require('lazy').setup({ ---@type blink.cmp.Config opts = { keymap = { - -- 'default' (recommended) for mappings similar to built-in completions - -- to accept ([y]es) the completion. - -- This will auto-import if your LSP supports it. - -- This will expand snippets if the LSP sent a snippet. - -- 'super-tab' for tab to accept - -- 'enter' for enter to accept - -- 'none' for no mappings - -- - -- For an understanding of why the 'default' preset is recommended, - -- you will need to read `:help ins-completion` - -- - -- No, but seriously. Please read `:help ins-completion`, it is really good! - -- - -- All presets have the following mappings: - -- /: move to right/left of your snippet expansion - -- : Open menu or open docs if already open - -- / or /: Select next/previous item - -- : Hide menu - -- : Toggle signature help - -- - -- See :h blink-cmp-config-keymap for defining your own keymap preset = 'default', - - -- For more advanced Luasnip keymaps (e.g. selecting choice nodes, expansion) see: - -- https://github.com/L3MON4D3/LuaSnip?tab=readme-ov-file#keymaps }, appearance = { - -- 'mono' (default) for 'Nerd Font Mono' or 'normal' for 'Nerd Font' - -- Adjusts spacing to ensure icons are aligned nerd_font_variant = 'mono', }, completion = { - -- By default, you may press `` to show the documentation. - -- Optionally, set `auto_show = true` to show the documentation after a delay. documentation = { auto_show = false, auto_show_delay_ms = 500 }, }, @@ -784,27 +682,15 @@ require('lazy').setup({ snippets = { preset = 'luasnip' }, - -- Blink.cmp includes an optional, recommended rust fuzzy matcher, - -- which automatically downloads a prebuilt binary when enabled. - -- - -- By default, we use the Lua implementation instead, but you may enable - -- the rust implementation via `'prefer_rust_with_warning'` - -- - -- See :h blink-cmp-config-fuzzy for more information fuzzy = { implementation = 'lua' }, - -- Shows a signature help window while you type arguments for a function signature = { enabled = true }, }, }, - { -- You can easily change to a different colorscheme. - -- Change the name of the colorscheme plugin below, and then - -- change the command in the config to whatever the name of that colorscheme is. - -- - -- If you want to see what colorschemes are already installed, you can use `:Telescope colorscheme`. + { 'folke/tokyonight.nvim', - priority = 1000, -- Make sure to load this before all the other start plugins. + priority = 1000, config = function() ---@diagnostic disable-next-line: missing-fields require('tokyonight').setup { @@ -813,9 +699,6 @@ require('lazy').setup({ }, } - -- Load the colorscheme here. - -- Like many other themes, this one has different styles, and you could load - -- any other, such as 'tokyonight-storm', 'tokyonight-moon', or 'tokyonight-day'. vim.cmd.colorscheme 'tokyonight-night' end, }, @@ -834,119 +717,57 @@ require('lazy').setup({ { -- Collection of various small independent plugins/modules 'nvim-mini/mini.nvim', config = function() - -- Better Around/Inside textobjects - -- - -- Examples: - -- - va) - [V]isually select [A]round [)]paren - -- - yinq - [Y]ank [I]nside [N]ext [Q]uote - -- - ci' - [C]hange [I]nside [']quote require('mini.ai').setup { n_lines = 500 } - - -- Add/delete/replace surroundings (brackets, quotes, etc.) - -- - -- - saiw) - [S]urround [A]dd [I]nner [W]ord [)]Paren - -- - sd' - [S]urround [D]elete [']quotes - -- - sr)' - [S]urround [R]eplace [)] ['] require('mini.surround').setup() - -- Simple and easy statusline. - -- You could remove this setup call if you don't like it, - -- and try some other statusline plugin + local bufremove = require 'mini.bufremove' + bufremove.setup() + vim.keymap.set('n', 'bd', function() bufremove.delete(0, false) end, { desc = '[B]uffer [D]elete' }) + local statusline = require 'mini.statusline' - -- set use_icons to true if you have a Nerd Font statusline.setup { use_icons = vim.g.have_nerd_font } - -- You can configure sections in the statusline by overriding their - -- default behavior. For example, here we set the section for - -- cursor location to LINE:COLUMN ---@diagnostic disable-next-line: duplicate-set-field statusline.section_location = function() return '%2l:%-2v' end - - -- ... and there is more! - -- Check out: https://github.com/nvim-mini/mini.nvim end, }, { -- Highlight, edit, and navigate code 'nvim-treesitter/nvim-treesitter', - lazy = false, - build = ':TSUpdate', branch = 'main', - -- [[ Configure Treesitter ]] See `:help nvim-treesitter-intro` - config = function() - -- ensure basic parser are installed - local parsers = { 'bash', 'c', 'diff', 'html', 'lua', 'luadoc', 'markdown', 'markdown_inline', 'query', 'vim', 'vimdoc' } - require('nvim-treesitter').install(parsers) - - ---@param buf integer - ---@param language string - local function treesitter_try_attach(buf, language) - -- check if parser exists and load it - if not vim.treesitter.language.add(language) then return end - -- enables syntax highlighting and other treesitter features - vim.treesitter.start(buf, language) - - -- enables treesitter based folds - -- for more info on folds see `:help folds` - -- vim.wo.foldexpr = 'v:lua.vim.treesitter.foldexpr()' - -- vim.wo.foldmethod = 'expr' - - -- enables treesitter based indentation - vim.bo.indentexpr = "v:lua.require'nvim-treesitter'.indentexpr()" + lazy = false, + build = function() + if vim.fn.executable 'tree-sitter' ~= 1 then + vim.notify('nvim-treesitter: tree-sitter CLI is required to install parsers', vim.log.levels.WARN) + return end - local available_parsers = require('nvim-treesitter').get_available() + local ok, treesitter = pcall(require, 'nvim-treesitter') + if ok then treesitter.install(treesitter_parsers, { summary = true }):wait(300000) end + end, + config = function() + local treesitter = require 'nvim-treesitter' + treesitter.setup() + + vim.api.nvim_create_user_command('TSInstallConfigured', function() + if vim.fn.executable 'tree-sitter' ~= 1 then + vim.notify('nvim-treesitter: install tree-sitter CLI first', vim.log.levels.ERROR) + return + end + + treesitter.install(treesitter_parsers, { summary = true }):raise_on_error() + end, { desc = 'Install configured Treesitter parsers' }) + vim.api.nvim_create_autocmd('FileType', { - callback = function(args) - local buf, filetype = args.buf, args.match - - local language = vim.treesitter.language.get_lang(filetype) - if not language then return end - - local installed_parsers = require('nvim-treesitter').get_installed 'parsers' - - if vim.tbl_contains(installed_parsers, language) then - -- enable the parser if it is installed - treesitter_try_attach(buf, language) - elseif vim.tbl_contains(available_parsers, language) then - -- if a parser is available in `nvim-treesitter` auto install it, and enable it after the installation is done - require('nvim-treesitter').install(language):await(function() treesitter_try_attach(buf, language) end) - else - -- try to enable treesitter features in case the parser exists but is not available from `nvim-treesitter` - treesitter_try_attach(buf, language) - end - end, + group = vim.api.nvim_create_augroup('kickstart-treesitter', { clear = true }), + callback = function() pcall(vim.treesitter.start) end, }) end, }, - -- The following comments only work if you have downloaded the kickstart repo, not just copy pasted the - -- init.lua. If you want these files, they are in the repository, so you can just download them and - -- place them in the correct locations. - - -- NOTE: Next step on your Neovim journey: Add/Configure additional plugins for Kickstart - -- - -- Here are some example plugins that I've included in the Kickstart repository. - -- Uncomment any of the lines below to enable them (you will need to restart nvim). - -- - -- require 'kickstart.plugins.debug', - -- require 'kickstart.plugins.indent_line', - -- require 'kickstart.plugins.lint', - -- require 'kickstart.plugins.autopairs', - -- require 'kickstart.plugins.neo-tree', - -- require 'kickstart.plugins.gitsigns', -- adds gitsigns recommended keymaps - - -- NOTE: The import below can automatically add your own plugins, configuration, etc from `lua/custom/plugins/*.lua` - -- This is the easiest way to modularize your config. - -- - -- Uncomment the following line and add your plugins to `lua/custom/plugins/*.lua` to get going. - -- { import = 'custom.plugins' }, - -- - -- For additional information with loading, sourcing and examples see `:help lazy.nvim-🔌-plugin-spec` - -- Or use telescope! - -- In normal mode type `sh` then write `lazy.nvim-plugin` - -- you can continue same window with `sr` which resumes last telescope search + { import = 'custom.plugins' }, }, { ---@diagnostic disable-line: missing-fields + rocks = { enabled = false }, ui = { -- If you are using a Nerd Font: set icons to an empty table which will use the -- default lazy.nvim defined Nerd Font icons, otherwise define a unicode icons table diff --git a/lua/kickstart/plugins/autopairs.lua b/lua/custom/plugins/autopairs.lua similarity index 100% rename from lua/kickstart/plugins/autopairs.lua rename to lua/custom/plugins/autopairs.lua diff --git a/lua/custom/plugins/bufferline.lua b/lua/custom/plugins/bufferline.lua new file mode 100644 index 00000000000..ded3d53cbdb --- /dev/null +++ b/lua/custom/plugins/bufferline.lua @@ -0,0 +1,31 @@ +return { + 'akinsho/bufferline.nvim', + event = 'VeryLazy', + dependencies = { + { 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font }, + }, + keys = { + { '', 'BufferLineCyclePrev', desc = 'Previous buffer' }, + { '', 'BufferLineCycleNext', desc = 'Next buffer' }, + { 'bp', 'BufferLinePick', desc = '[B]uffer [P]ick' }, + { + 'bD', + function() + local bufremove = require 'mini.bufremove' + + for _, bufnr in ipairs(vim.api.nvim_list_bufs()) do + if vim.api.nvim_buf_is_valid(bufnr) and vim.bo[bufnr].buflisted and vim.bo[bufnr].filetype ~= 'neo-tree' then bufremove.delete(bufnr, false) end + end + end, + desc = '[B]uffer [D]elete all', + }, + }, + opts = { + options = { + mode = 'buffers', + always_show_bufferline = true, + show_buffer_close_icons = false, + show_close_icon = false, + }, + }, +} diff --git a/lua/custom/plugins/cmake_tools.lua b/lua/custom/plugins/cmake_tools.lua new file mode 100644 index 00000000000..b532dc2dd37 --- /dev/null +++ b/lua/custom/plugins/cmake_tools.lua @@ -0,0 +1,29 @@ +return { + 'Civitasv/cmake-tools.nvim', + dependencies = { 'nvim-lua/plenary.nvim' }, + cmd = { + 'CMakeGenerate', + 'CMakeBuild', + 'CMakeRun', + 'CMakeTest', + 'CMakeSelectBuildType', + 'CMakeSelectBuildTarget', + }, + ft = { 'cmake' }, + keys = { + { 'cg', 'CMakeGenerate', desc = '[C]Make [G]enerate' }, + { 'cb', 'CMakeBuild', desc = '[C]Make [B]uild' }, + { 'cr', 'CMakeRun', desc = '[C]Make [R]un' }, + { 'ct', 'CMakeTest', desc = '[C]Make [T]est' }, + { 'cc', 'CMakeSelectBuildType', desc = '[C]Make [C]onfiguration' }, + }, + opts = { + cmake_generate_options = { '-DCMAKE_EXPORT_COMPILE_COMMANDS=1' }, + cmake_executor = { + name = 'quickfix', + }, + cmake_runner = { + name = 'terminal', + }, + }, +} diff --git a/lua/custom/plugins/dap_js_ts.lua b/lua/custom/plugins/dap_js_ts.lua new file mode 100644 index 00000000000..8dc13a726ad --- /dev/null +++ b/lua/custom/plugins/dap_js_ts.lua @@ -0,0 +1,40 @@ +return { + 'mxsdev/nvim-dap-vscode-js', + dependencies = { 'mfussenegger/nvim-dap' }, + ft = { 'javascript', 'javascriptreact', 'typescript', 'typescriptreact', 'vue' }, + config = function() + require('dap-vscode-js').setup { + debugger_cmd = { 'js-debug-adapter' }, + adapters = { + 'pwa-node', + 'pwa-chrome', + 'pwa-msedge', + 'node-terminal', + 'pwa-extensionHost', + }, + } + + local dap = require 'dap' + local configs = { + { + type = 'pwa-node', + request = 'launch', + name = 'Launch current file (Node)', + program = '${file}', + cwd = '${workspaceFolder}', + sourceMaps = true, + }, + { + type = 'pwa-node', + request = 'attach', + name = 'Attach to process', + processId = require('dap.utils').pick_process, + cwd = '${workspaceFolder}', + }, + } + + for _, language in ipairs { 'javascript', 'javascriptreact', 'typescript', 'typescriptreact', 'vue' } do + dap.configurations[language] = vim.list_extend(dap.configurations[language] or {}, configs) + end + end, +} diff --git a/lua/custom/plugins/debug.lua b/lua/custom/plugins/debug.lua new file mode 100644 index 00000000000..e762660b7a6 --- /dev/null +++ b/lua/custom/plugins/debug.lua @@ -0,0 +1,98 @@ +local function has_configuration(configurations, name, adapter) + for _, configuration in ipairs(configurations or {}) do + if configuration.name == name and configuration.type == adapter then return true end + end + + return false +end + +local function setup_cpp_dap(dap) + dap.adapters.codelldb = dap.adapters.codelldb or { + type = 'executable', + command = 'codelldb', + } + + local launch_name = 'Launch current file (codelldb)' + local launch_configuration = { + name = launch_name, + type = 'codelldb', + request = 'launch', + program = function() return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file') end, + cwd = '${workspaceFolder}', + stopOnEntry = false, + args = {}, + } + + for _, language in ipairs { 'c', 'cpp' } do + dap.configurations[language] = dap.configurations[language] or {} + + if not has_configuration(dap.configurations[language], launch_name, 'codelldb') then + table.insert(dap.configurations[language], vim.deepcopy(launch_configuration)) + end + end +end + +---@module 'lazy' +---@type LazySpec +return { + 'mfussenegger/nvim-dap', + dependencies = { + 'rcarriga/nvim-dap-ui', + 'nvim-neotest/nvim-nio', + 'mason-org/mason.nvim', + 'jay-babu/mason-nvim-dap.nvim', + 'leoluz/nvim-dap-go', + }, + keys = { + { '', function() require('dap').continue() end, desc = 'Debug: Start/Continue' }, + { '', function() require('dap').step_into() end, desc = 'Debug: Step Into' }, + { '', function() require('dap').step_over() end, desc = 'Debug: Step Over' }, + { '', function() require('dap').step_out() end, desc = 'Debug: Step Out' }, + { 'b', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint' }, + { 'B', function() require('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. + { '', function() require('dapui').toggle() end, desc = 'Debug: See last session result.' }, + }, + config = function() + local dap = require 'dap' + local dapui = require 'dapui' + local has_delve = vim.fn.executable 'dlv' == 1 + + require('mason-nvim-dap').setup { + automatic_installation = true, + handlers = {}, + ensure_installed = { 'codelldb' }, + } + + ---@diagnostic disable-next-line: missing-fields + dapui.setup { + icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, + ---@diagnostic disable-next-line: missing-fields + controls = { + icons = { + pause = '⏸', + play = '▶', + step_into = '⏎', + step_over = '⏭', + step_out = '⏮', + step_back = 'b', + run_last = '▶▶', + terminate = '⏹', + disconnect = '⏏', + }, + }, + } + + 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 + + if has_delve then require('dap-go').setup { + delve = { + detached = vim.fn.has 'win32' == 0, + }, + } end + + setup_cpp_dap(dap) + end, +} diff --git a/lua/custom/plugins/diffview.lua b/lua/custom/plugins/diffview.lua new file mode 100644 index 00000000000..b893a0c8898 --- /dev/null +++ b/lua/custom/plugins/diffview.lua @@ -0,0 +1,19 @@ +return { + 'sindrets/diffview.nvim', + dependencies = { 'nvim-lua/plenary.nvim' }, + cmd = { + 'DiffviewOpen', + 'DiffviewClose', + 'DiffviewFileHistory', + 'DiffviewFocusFiles', + 'DiffviewToggleFiles', + 'DiffviewRefresh', + }, + keys = { + { 'Gd', 'DiffviewOpen', desc = '[G]it [D]iff view' }, + { 'GD', 'DiffviewClose', desc = '[G]it [D]iff close' }, + { 'Gf', 'DiffviewFileHistory %', desc = '[G]it [F]ile history' }, + { 'GF', 'DiffviewFileHistory', desc = '[G]it [F]ull history' }, + }, + opts = {}, +} diff --git a/lua/custom/plugins/folding.lua b/lua/custom/plugins/folding.lua new file mode 100644 index 00000000000..c6cf7c21908 --- /dev/null +++ b/lua/custom/plugins/folding.lua @@ -0,0 +1,27 @@ +return { + { + 'kevinhwang91/nvim-ufo', + dependencies = { + 'kevinhwang91/promise-async', + }, + event = 'VeryLazy', + init = function() + vim.o.foldcolumn = '1' + vim.o.foldlevel = 99 + vim.o.foldlevelstart = 99 + vim.o.foldenable = true + end, + opts = { + provider_selector = function(_, _, _) + return { 'treesitter', 'indent' } + end, + }, + config = function(_, opts) + local ufo = require 'ufo' + ufo.setup(opts) + + vim.keymap.set('n', 'zR', ufo.openAllFolds, { desc = 'Open all folds' }) + vim.keymap.set('n', 'zM', ufo.closeAllFolds, { desc = 'Close all folds' }) + end, + }, +} diff --git a/lua/kickstart/plugins/gitsigns.lua b/lua/custom/plugins/gitsigns.lua similarity index 92% rename from lua/kickstart/plugins/gitsigns.lua rename to lua/custom/plugins/gitsigns.lua index 1d8c50c37db..99d64a3437a 100644 --- a/lua/kickstart/plugins/gitsigns.lua +++ b/lua/custom/plugins/gitsigns.lua @@ -1,6 +1,4 @@ -- Adds git related signs to the gutter, as well as utilities for managing changes --- NOTE: gitsigns is already included in init.lua but contains only the base --- config. This will add also the recommended keymaps. ---@module 'lazy' ---@type LazySpec @@ -44,6 +42,7 @@ return { map('n', 'hs', gitsigns.stage_hunk, { desc = 'git [s]tage hunk' }) map('n', 'hr', gitsigns.reset_hunk, { desc = 'git [r]eset hunk' }) map('n', 'hS', gitsigns.stage_buffer, { desc = 'git [S]tage buffer' }) + map('n', 'hu', gitsigns.undo_stage_hunk, { desc = 'git [u]ndo stage hunk' }) map('n', 'hR', gitsigns.reset_buffer, { desc = 'git [R]eset buffer' }) map('n', 'hp', gitsigns.preview_hunk, { desc = 'git [p]review hunk' }) map('n', 'hi', gitsigns.preview_hunk_inline, { desc = 'git preview hunk [i]nline' }) @@ -55,9 +54,7 @@ return { -- Toggles map('n', 'tb', gitsigns.toggle_current_line_blame, { desc = '[T]oggle git show [b]lame line' }) map('n', 'tw', gitsigns.toggle_word_diff) - - -- Text object - map({ 'o', 'x' }, 'ih', gitsigns.select_hunk) + map('n', 'tD', gitsigns.toggle_deleted, { desc = '[T]oggle git show [D]eleted' }) end, }, } diff --git a/lua/custom/plugins/grug_far.lua b/lua/custom/plugins/grug_far.lua new file mode 100644 index 00000000000..7023842f7a0 --- /dev/null +++ b/lua/custom/plugins/grug_far.lua @@ -0,0 +1,27 @@ +return { + 'MagicDuck/grug-far.nvim', + cmd = { 'GrugFar', 'GrugFarWithin' }, + opts = {}, + keys = { + { + 'sR', + function() + require('grug-far').open { + prefills = { + search = vim.fn.expand '', + }, + } + end, + mode = 'n', + desc = '[S]earch project [R]eplace', + }, + { + 'sR', + function() + require('grug-far').with_visual_selection() + end, + mode = 'x', + desc = '[S]earch project [R]eplace selection', + }, + }, +} diff --git a/lua/custom/plugins/harpoon.lua b/lua/custom/plugins/harpoon.lua new file mode 100644 index 00000000000..21ec13d6482 --- /dev/null +++ b/lua/custom/plugins/harpoon.lua @@ -0,0 +1,72 @@ +return { + 'ThePrimeagen/harpoon', + branch = 'harpoon2', + dependencies = { 'nvim-lua/plenary.nvim' }, + config = function() + local harpoon = require 'harpoon' + harpoon:setup { + default = { + select = function(list_item, _, options) + if not list_item or not list_item.value or list_item.value == '' then + return + end + + options = options or {} + local open_cmd = 'edit' + if options.vsplit then + open_cmd = 'vsplit' + elseif options.split then + open_cmd = 'split' + elseif options.tabedit then + open_cmd = 'tabedit' + end + + vim.cmd(open_cmd .. ' ' .. vim.fn.fnameescape(list_item.value)) + + local context = list_item.context or {} + local row = math.max(1, tonumber(context.row) or 1) + local col = math.max(0, tonumber(context.col) or 0) + local line_count = vim.api.nvim_buf_line_count(0) + if line_count < 1 then + return + end + + if row > line_count then + row = line_count + end + + local row_text = vim.api.nvim_buf_get_lines(0, row - 1, row, false)[1] or '' + if col > #row_text then + col = #row_text + end + + pcall(vim.api.nvim_win_set_cursor, 0, { row, col }) + end, + }, + } + + vim.keymap.set('n', 'a', function() + if vim.bo.buftype ~= '' then + vim.notify('Harpoon: current buffer is not a file', vim.log.levels.INFO) + return + end + harpoon:list():add() + end, { desc = 'Harpoon add' }) + vim.keymap.set('n', 'hm', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Harpoon menu' }) + vim.keymap.set('n', '', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Harpoon menu' }) + vim.api.nvim_create_user_command('HarpoonMenu', function() + harpoon.ui:toggle_quick_menu(harpoon:list()) + end, { desc = 'Toggle Harpoon quick menu' }) + + vim.keymap.set('n', '', function() + harpoon:list():prev() + end) + vim.keymap.set('n', '', function() + harpoon:list():next() + end) + end, +} diff --git a/lua/custom/plugins/helm.lua b/lua/custom/plugins/helm.lua new file mode 100644 index 00000000000..d4b5e1b4bf4 --- /dev/null +++ b/lua/custom/plugins/helm.lua @@ -0,0 +1,3 @@ +return { + 'towolf/vim-helm', +} diff --git a/lua/kickstart/plugins/indent_line.lua b/lua/custom/plugins/indent_line.lua similarity index 100% rename from lua/kickstart/plugins/indent_line.lua rename to lua/custom/plugins/indent_line.lua diff --git a/lua/custom/plugins/init.lua b/lua/custom/plugins/init.lua index b3ddcfdd3aa..a2ded374463 100644 --- a/lua/custom/plugins/init.lua +++ b/lua/custom/plugins/init.lua @@ -1,8 +1,3 @@ --- You can add your own plugins here or in other files in this directory! --- I promise not to create any merge conflicts in this directory :) --- --- See the kickstart.nvim README for more information - ---@module 'lazy' ---@type LazySpec return {} diff --git a/lua/custom/plugins/lint.lua b/lua/custom/plugins/lint.lua new file mode 100644 index 00000000000..3a355d854a7 --- /dev/null +++ b/lua/custom/plugins/lint.lua @@ -0,0 +1,86 @@ +return { + + { -- Linting + 'mfussenegger/nvim-lint', + event = { 'BufReadPre', 'BufNewFile' }, + config = function() + local lint = require 'lint' + lint.linters_by_ft = { + markdown = { 'markdownlint' }, + dockerfile = { 'hadolint' }, + yaml = { 'yamllint' }, + ['yaml.helm-values'] = { 'yamllint' }, + } + lint.linters.markdownlint = vim.tbl_deep_extend('force', lint.linters.markdownlint or {}, { + args = { + '--stdin', + '--disable', + 'MD013', -- line length + 'MD033', -- inline HTML + 'MD041', -- first line heading + }, + }) + + local function executable_cmd(cmd) + if type(cmd) == 'function' then + local ok, value = pcall(cmd) + if not ok then return nil end + return value + end + return cmd + end + + local function available_linters_for(ft) + local configured = lint.linters_by_ft[ft] or {} + local available = {} + for _, linter_name in ipairs(configured) do + local linter = lint.linters[linter_name] + local cmd = linter and executable_cmd(linter.cmd) + if cmd == nil or cmd == '' or vim.fn.executable(cmd) == 1 then table.insert(available, linter_name) end + end + return available + end + + local function trigger_lint() + if vim.bo.modifiable then + local linters = available_linters_for(vim.bo.filetype) + if #linters > 0 then lint.try_lint(linters) end + end + end + + -- Toggle lint on/off + local lint_enabled = true + vim.keymap.set('n', 'tl', function() + lint_enabled = not lint_enabled + if not lint_enabled then + local cleared = {} + for _, linters in pairs(lint.linters_by_ft) do + for _, linter_name in ipairs(linters) do + local ns = lint.get_namespace(linter_name) + if not cleared[ns] then + vim.diagnostic.reset(ns) + cleared[ns] = true + end + end + end + else + trigger_lint() + end + vim.notify('Lint ' .. (lint_enabled and 'enabled' or 'disabled')) + end, { desc = '[T]oggle [L]int' }) + + -- Create autocommand which carries out the actual linting + -- on the specified events. + local lint_augroup = vim.api.nvim_create_augroup('lint', { clear = true }) + vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { + group = lint_augroup, + -- Only run the linter in buffers that you can modify in order to + -- avoid superfluous noise, notably within the handy LSP pop-ups that + -- describe the hovered symbol using Markdown. + callback = function() + if lint_enabled then trigger_lint() end + end, + }) + end, + }, +} diff --git a/lua/custom/plugins/location_reference.lua b/lua/custom/plugins/location_reference.lua new file mode 100644 index 00000000000..1b44725161f --- /dev/null +++ b/lua/custom/plugins/location_reference.lua @@ -0,0 +1,274 @@ +local sk = vim.lsp.protocol.SymbolKind +local symbol_kinds = { + [sk.Class] = true, + [sk.Method] = true, + [sk.Constructor] = true, + [sk.Enum] = true, + [sk.Interface] = true, + [sk.Function] = true, + [sk.Struct] = true, +} + +local function range_contains(range, line0, col0) + if not range then + return false + end + + local start_line = range.start.line + local start_col = range.start.character + local end_line = range['end'].line + local end_col = range['end'].character + + if line0 < start_line or line0 > end_line then + return false + end + if line0 == start_line and col0 < start_col then + return false + end + if line0 == end_line and col0 > end_col then + return false + end + + return true +end + +local function range_size(range) + -- Weight lines heavily so a single-line range is always smaller than a multi-line one + return (range['end'].line - range.start.line) * 100000 + (range['end'].character - range.start.character) +end + +local function append_document_symbols(symbols, out, parent_name) + for _, symbol in ipairs(symbols or {}) do + local symbol_name = symbol.name + local full_name = parent_name and (parent_name .. '.' .. symbol_name) or symbol_name + table.insert(out, { + name = full_name, + kind = symbol.kind, + range = symbol.range, + }) + append_document_symbols(symbol.children, out, full_name) + end +end + +local function append_symbol_information(symbols, out) + for _, symbol in ipairs(symbols or {}) do + local name = symbol.name + if symbol.containerName and symbol.containerName ~= '' then + name = symbol.containerName .. '.' .. name + end + table.insert(out, { + name = name, + kind = symbol.kind, + range = symbol.location and symbol.location.range, + }) + end +end + +local function get_symbol_at_cursor() + if #vim.lsp.get_clients { bufnr = 0 } == 0 then + return nil + end + + local params = { textDocument = vim.lsp.util.make_text_document_params() } + local responses = vim.lsp.buf_request_sync(0, 'textDocument/documentSymbol', params, 500) + if not responses then + return nil + end + + local cursor = vim.api.nvim_win_get_cursor(0) + local line0 = cursor[1] - 1 + -- LSP ranges use UTF-16 offsets; convert from byte offset for correct matching + -- on files with non-ASCII characters (e.g. CJK, emoji). + local line_text = vim.api.nvim_buf_get_lines(0, line0, line0 + 1, false)[1] or '' + local col0 = vim.str_utfindex(line_text, 'utf-16', cursor[2]) + local symbols = {} + + for _, response in pairs(responses) do + if not response.error then + local result = response.result + if type(result) == 'table' and result[1] then + if result[1].selectionRange or result[1].children then + append_document_symbols(result, symbols, nil) + else + append_symbol_information(result, symbols) + end + end + end + end + + local best_symbol, best_size + for _, symbol in ipairs(symbols) do + if symbol_kinds[symbol.kind] and range_contains(symbol.range, line0, col0) then + local size = range_size(symbol.range) + if not best_size or size < best_size then + best_size = size + best_symbol = symbol.name + end + end + end + + return best_symbol +end + +local function get_relative_path_from_root(file_path) + local root = vim.fs.root(file_path, { '.git' }) or vim.fn.getcwd() + + if vim.fs.relpath then + local rel = vim.fs.relpath(root, file_path) + if rel then + return rel + end + end + + local prefix = root + if not prefix:match '/$' then + prefix = prefix .. '/' + end + if file_path:sub(1, #prefix) == prefix then + return file_path:sub(#prefix + 1) + end + + return file_path +end + +local function get_file_parts() + local file_path = vim.api.nvim_buf_get_name(0) + if file_path == '' then + vim.notify('No file path for current buffer', vim.log.levels.WARN) + return nil + end + + return { + file_path = file_path, + file_rel = get_relative_path_from_root(file_path), + } +end + +local function get_location_parts() + local parts = get_file_parts() + if not parts then + return nil + end + + local mode = vim.api.nvim_get_mode().mode + local loc + + if mode == 'v' or mode == 'V' or mode == '\22' then + -- Use 'v' (visual anchor) and '.' (cursor) for the live selection. + -- ' marks are only reliable after exiting visual mode (e.g. ':' mappings), + -- but mappings stay in visual mode so those marks are stale. + local start_pos = vim.fn.getpos 'v' + local end_pos = vim.fn.getpos '.' + local s_line, s_col = start_pos[2], start_pos[3] + local e_line, e_col = end_pos[2], end_pos[3] + + -- getpos('v') returns zeros when called outside a real visual context; fall back. + if s_line == 0 then + local cur = vim.api.nvim_win_get_cursor(0) + s_line, s_col = cur[1], cur[2] + 1 + e_line, e_col = s_line, s_col + elseif s_line > e_line or (s_line == e_line and s_col > e_col) then + s_line, e_line = e_line, s_line + s_col, e_col = e_col, s_col + end + + loc = string.format('%d:%d-%d:%d', s_line, s_col, e_line, e_col) + else + local cursor = vim.api.nvim_win_get_cursor(0) + local line_num = cursor[1] + local line_text = vim.api.nvim_buf_get_lines(0, line_num - 1, line_num, false)[1] or '' + local end_col = math.max(vim.fn.strchars(line_text), 1) + loc = string.format('%d:%d-%d:%d', line_num, 1, line_num, end_col) + end + + return { + file_path = parts.file_path, + file_rel = parts.file_rel, + loc = loc, + symbol = get_symbol_at_cursor(), + } +end + +local function yank(label, value) + vim.fn.setreg('"', value) + pcall(vim.fn.setreg, '+', value) + vim.notify('Copied ' .. label .. ': ' .. value, vim.log.levels.INFO) +end + +local function copy_absolute_location_reference() + local parts = get_location_parts() + if not parts then + return + end + + local location = string.format('%s:%s', parts.file_path, parts.loc) + if parts.symbol and parts.symbol ~= '' then + location = location .. ' symbol:' .. parts.symbol + end + + yank('absolute location', location) +end + +local function copy_relative_location_reference() + local parts = get_location_parts() + if not parts then + return + end + + local symbol = parts.symbol and parts.symbol ~= '' and parts.symbol or 'none' + yank('relative location', string.format('@%s loc:%s symbol:%s', parts.file_rel, parts.loc, symbol)) +end + +local function copy_buffer_file_reference() + local parts = get_file_parts() + if not parts then + return + end + + yank('buffer file', string.format('@%s', parts.file_rel)) +end + +local function copy_buffer_directory_reference() + local parts = get_file_parts() + if not parts then + return + end + + local abs_dir = vim.fn.fnamemodify(parts.file_path, ':h') + local rel_dir = get_relative_path_from_root(abs_dir) + yank('buffer directory', string.format('@%s', rel_dir)) +end + +return { + 'folke/which-key.nvim', + keys = { + { + 'ya', + copy_absolute_location_reference, + mode = { 'n', 'x' }, + desc = '[Y]ank [A]bsolute ref', + }, + { + 'yr', + copy_relative_location_reference, + mode = { 'n', 'x' }, + desc = '[Y]ank [R]elative ref', + }, + { + 'yf', + copy_buffer_file_reference, + mode = { 'n', 'x' }, + desc = '[Y]ank Buffer [F]ile', + }, + { + 'yd', + copy_buffer_directory_reference, + mode = { 'n', 'x' }, + desc = '[Y]ank Buffer [D]irectory', + }, + }, + opts = function(_, opts) + opts.spec = opts.spec or {} + table.insert(opts.spec, { 'y', group = '[Y]ank' }) + end, +} diff --git a/lua/custom/plugins/lsp_file_operations.lua b/lua/custom/plugins/lsp_file_operations.lua new file mode 100644 index 00000000000..3f27b8eefe9 --- /dev/null +++ b/lua/custom/plugins/lsp_file_operations.lua @@ -0,0 +1,13 @@ +return { + 'antosha417/nvim-lsp-file-operations', + dependencies = { 'nvim-neo-tree/neo-tree.nvim' }, + config = function() + require('lsp-file-operations').setup() + + -- Advertise file operation capabilities to all LSP servers + local file_ops_caps = require('lsp-file-operations').default_capabilities() + vim.lsp.config('*', { + capabilities = file_ops_caps, + }) + end, +} diff --git a/lua/custom/plugins/markdown.lua b/lua/custom/plugins/markdown.lua new file mode 100644 index 00000000000..f5beefa0b94 --- /dev/null +++ b/lua/custom/plugins/markdown.lua @@ -0,0 +1,47 @@ +return { + { + 'MeanderingProgrammer/render-markdown.nvim', + ft = { 'markdown' }, + dependencies = { + 'nvim-treesitter/nvim-treesitter', + { 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font }, + }, + opts = { + file_types = { 'markdown' }, + }, + }, + { + 'iamcco/markdown-preview.nvim', + ft = { 'markdown' }, + cmd = { 'MarkdownPreviewToggle', 'MarkdownPreview', 'MarkdownPreviewStop' }, + build = function() vim.fn['mkdp#util#install']() end, + init = function() + local browser = '' + local is_macos = vim.fn.has 'macunix' == 1 + + if is_macos then + vim.cmd [[ + function! OpenMarkdownPreview(url) abort + execute 'silent !open ' . shellescape(a:url) + endfunction + ]] + elseif vim.fn.executable 'google-chrome-stable' == 1 then + browser = 'google-chrome-stable' + elseif vim.fn.executable 'google-chrome' == 1 then + browser = 'google-chrome' + elseif vim.fn.executable 'chromium' == 1 then + browser = 'chromium' + end + + vim.g.mkdp_auto_start = 0 + vim.g.mkdp_auto_close = 1 + vim.g.mkdp_refresh_slow = 0 + vim.g.mkdp_browser = browser + vim.g.mkdp_browserfunc = is_macos and 'OpenMarkdownPreview' or '' + vim.g.mkdp_filetypes = { 'markdown' } + end, + keys = { + { 'mp', 'MarkdownPreviewToggle', desc = '[M]arkdown [P]review' }, + }, + }, +} diff --git a/lua/kickstart/plugins/neo-tree.lua b/lua/custom/plugins/neo-tree.lua similarity index 88% rename from lua/kickstart/plugins/neo-tree.lua rename to lua/custom/plugins/neo-tree.lua index af8d4495650..0ac053b979e 100644 --- a/lua/kickstart/plugins/neo-tree.lua +++ b/lua/custom/plugins/neo-tree.lua @@ -8,7 +8,7 @@ return { version = '*', dependencies = { 'nvim-lua/plenary.nvim', - 'nvim-tree/nvim-web-devicons', -- not strictly required, but recommended + { 'nvim-tree/nvim-web-devicons', enabled = vim.g.have_nerd_font }, 'MunifTanjim/nui.nvim', }, lazy = false, diff --git a/lua/custom/plugins/neogit.lua b/lua/custom/plugins/neogit.lua new file mode 100644 index 00000000000..784b71be1e2 --- /dev/null +++ b/lua/custom/plugins/neogit.lua @@ -0,0 +1,13 @@ +return { + 'NeogitOrg/neogit', + lazy = true, + dependencies = { + 'nvim-lua/plenary.nvim', + 'sindrets/diffview.nvim', + 'nvim-telescope/telescope.nvim', + }, + cmd = 'Neogit', + keys = { + { 'Gg', 'Neogit', desc = 'Show Neogit UI' }, + }, +} diff --git a/lua/custom/plugins/neotest.lua b/lua/custom/plugins/neotest.lua new file mode 100644 index 00000000000..1b0d2937b6f --- /dev/null +++ b/lua/custom/plugins/neotest.lua @@ -0,0 +1,98 @@ +return { + 'nvim-neotest/neotest', + dependencies = { + 'nvim-neotest/nvim-nio', + 'nvim-lua/plenary.nvim', + 'nvim-treesitter/nvim-treesitter', + 'nvim-neotest/neotest-jest', + 'marilari88/neotest-vitest', + 'alfaix/neotest-gtest', + }, + keys = { + { + 'nr', + function() + require('neotest').run.run() + end, + desc = '[N]eotest run nea[r]est', + }, + { + 'nf', + function() + require('neotest').run.run(vim.fn.expand '%') + end, + desc = '[N]eotest run [F]ile', + }, + { + 'ns', + function() + require('neotest').run.run(vim.fn.getcwd()) + end, + desc = '[N]eotest run [S]uite', + }, + { + 'nd', + function() + require('neotest').run.run { strategy = 'dap' } + end, + desc = '[N]eotest [D]ebug nearest', + }, + { + 'nn', + function() + require('neotest').summary.toggle() + end, + desc = '[N]eotest summary', + }, + { + 'no', + function() + require('neotest').output.open { enter = true, auto_close = true } + end, + desc = '[N]eotest [O]utput', + }, + { + 'nO', + function() + require('neotest').output_panel.toggle() + end, + desc = '[N]eotest [O]utput panel', + }, + { + 'na', + function() + require('neotest').run.attach() + end, + desc = '[N]eotest [A]ttach', + }, + { + 'nS', + function() + require('neotest').run.stop() + end, + desc = '[N]eotest [S]top', + }, + }, + config = function() + local adapters = {} + + local ok_jest, jest = pcall(require, 'neotest-jest') + if ok_jest then + table.insert(adapters, jest {}) + end + + local ok_vitest, vitest = pcall(require, 'neotest-vitest') + if ok_vitest then + table.insert(adapters, vitest {}) + end + + local ok_gtest, gtest = pcall(require, 'neotest-gtest') + if ok_gtest then + table.insert(adapters, gtest.setup {}) + end + + require('neotest').setup { + adapters = adapters, + } + end, +} diff --git a/lua/custom/plugins/oil.lua b/lua/custom/plugins/oil.lua new file mode 100644 index 00000000000..c4fb1abd068 --- /dev/null +++ b/lua/custom/plugins/oil.lua @@ -0,0 +1,19 @@ +return { + 'stevearc/oil.nvim', + cmd = { 'Oil' }, + keys = { + { 'eo', 'Oil', desc = '[E]xplorer [O]il' }, + }, + opts = { + default_file_explorer = false, + columns = { 'icon' }, + view_options = { + show_hidden = true, + }, + lsp_file_methods = { + enabled = true, + timeout_ms = 2000, + autosave_changes = 'unmodified', + }, + }, +} diff --git a/lua/custom/plugins/persistence.lua b/lua/custom/plugins/persistence.lua new file mode 100644 index 00000000000..5c0763d3117 --- /dev/null +++ b/lua/custom/plugins/persistence.lua @@ -0,0 +1,30 @@ +return { + 'folke/persistence.nvim', + event = 'BufReadPre', + opts = { + options = { 'buffers', 'curdir', 'tabpages', 'winsize', 'help', 'globals' }, + }, + keys = { + { + 'wr', + function() + require('persistence').load() + end, + desc = '[W]orkspace [R]estore session', + }, + { + 'wl', + function() + require('persistence').load { last = true } + end, + desc = '[W]orkspace [L]ast session', + }, + { + 'wd', + function() + require('persistence').stop() + end, + desc = '[W]orkspace [D]isable session save', + }, + }, +} diff --git a/lua/custom/plugins/supermaven.lua b/lua/custom/plugins/supermaven.lua new file mode 100644 index 00000000000..29be06968cc --- /dev/null +++ b/lua/custom/plugins/supermaven.lua @@ -0,0 +1,22 @@ +return { + 'supermaven-inc/supermaven-nvim', + event = 'InsertEnter', + config = function() + require('supermaven-nvim').setup { + keymaps = { + accept_suggestion = '', + clear_suggestion = '', + accept_word = '', + }, + ignore_filetypes = { cpp = true }, -- or { "cpp", } + color = { + suggestion_color = '#ffffff', + cterm = 244, + }, + log_level = 'off', + disable_inline_completion = false, -- disables inline completion for use with cmp + disable_keymaps = false, -- disables built in keymaps for more manual control + condition = function() return false end, -- condition to check for stopping supermaven, `true` means to stop supermaven when the condition is true. + } + end, +} diff --git a/lua/custom/plugins/tmux_navigator.lua b/lua/custom/plugins/tmux_navigator.lua new file mode 100644 index 00000000000..26fb72ba14c --- /dev/null +++ b/lua/custom/plugins/tmux_navigator.lua @@ -0,0 +1,18 @@ +return { + 'christoomey/vim-tmux-navigator', + cmd = { + 'TmuxNavigateLeft', + 'TmuxNavigateDown', + 'TmuxNavigateUp', + 'TmuxNavigateRight', + 'TmuxNavigatePrevious', + 'TmuxNavigatorProcessList', + }, + keys = { + { '', 'TmuxNavigateLeft' }, + { '', 'TmuxNavigateDown' }, + { '', 'TmuxNavigateUp' }, + { '', 'TmuxNavigateRight' }, + { '', 'TmuxNavigatePrevious' }, + }, +} diff --git a/lua/custom/plugins/treesitter_context.lua b/lua/custom/plugins/treesitter_context.lua new file mode 100644 index 00000000000..2904e80767c --- /dev/null +++ b/lua/custom/plugins/treesitter_context.lua @@ -0,0 +1,17 @@ +return { + 'nvim-treesitter/nvim-treesitter-context', + opts = { + max_lines = 4, + multiline_threshold = 20, + trim_scope = 'outer', + }, + keys = { + { + 'tc', + function() + require('treesitter-context').toggle() + end, + desc = '[T]oggle code [C]ontext', + }, + }, +} diff --git a/lua/custom/plugins/treesitter_textobjects.lua b/lua/custom/plugins/treesitter_textobjects.lua new file mode 100644 index 00000000000..f904c5b70b2 --- /dev/null +++ b/lua/custom/plugins/treesitter_textobjects.lua @@ -0,0 +1,47 @@ +return { + 'nvim-treesitter/nvim-treesitter-textobjects', + branch = 'main', + dependencies = { 'nvim-treesitter/nvim-treesitter' }, + config = function() + require('nvim-treesitter-textobjects').setup { + select = { + lookahead = true, + keymaps = { + ['af'] = '@function.outer', + ['if'] = '@function.inner', + ['ac'] = '@class.outer', + ['ic'] = '@class.inner', + }, + }, + move = { + set_jumps = true, + }, + } + + local move = require 'nvim-treesitter-textobjects.move' + + vim.keymap.set('n', 'jm', function() + move.goto_next_start '@function.outer' + end, { desc = '[J]ump next [M]ethod start' }) + + vim.keymap.set('n', 'jM', function() + move.goto_next_end '@function.outer' + end, { desc = '[J]ump next [M]ethod end' }) + + vim.keymap.set('n', 'jk', function() + move.goto_previous_start '@function.outer' + end, { desc = '[J]ump previous method start' }) + + vim.keymap.set('n', 'jK', function() + move.goto_previous_end '@function.outer' + end, { desc = '[J]ump previous method end' }) + + vim.keymap.set('n', 'jc', function() + move.goto_next_start '@class.outer' + end, { desc = '[J]ump next [C]lass start' }) + + vim.keymap.set('n', 'jC', function() + move.goto_previous_start '@class.outer' + end, { desc = '[J]ump previous class start' }) + end, +} diff --git a/lua/custom/plugins/trouble.lua b/lua/custom/plugins/trouble.lua new file mode 100644 index 00000000000..d47f80a791b --- /dev/null +++ b/lua/custom/plugins/trouble.lua @@ -0,0 +1,45 @@ +return { + 'folke/trouble.nvim', + cmd = { 'Trouble' }, + opts = {}, + keys = { + { + 'xx', + function() + require('trouble').toggle 'diagnostics' + end, + desc = 'Trouble: all diagnostics', + }, + { + 'xw', + function() + require('trouble').toggle 'diagnostics' + end, + desc = 'Trouble: [W]orkspace diagnostics', + }, + { + 'xd', + function() + require('trouble').toggle { + mode = 'diagnostics', + filter = { buf = 0 }, + } + end, + desc = 'Trouble: [D]ocument diagnostics', + }, + { + 'xq', + function() + require('trouble').toggle 'qflist' + end, + desc = 'Trouble: [Q]uickfix list', + }, + { + 'xl', + function() + require('trouble').toggle 'loclist' + end, + desc = 'Trouble: [L]ocation list', + }, + }, +} diff --git a/lua/custom/plugins/typescript_tools.lua b/lua/custom/plugins/typescript_tools.lua new file mode 100644 index 00000000000..fb036b6aeef --- /dev/null +++ b/lua/custom/plugins/typescript_tools.lua @@ -0,0 +1,12 @@ +return { + 'pmizio/typescript-tools.nvim', + dependencies = { 'nvim-lua/plenary.nvim', 'neovim/nvim-lspconfig' }, + opts = { + filetypes = { + 'javascript', + 'javascriptreact', + 'typescript', + 'typescriptreact', + }, + }, +} diff --git a/lua/kickstart/health.lua b/lua/kickstart/health.lua deleted file mode 100644 index ca684516003..00000000000 --- a/lua/kickstart/health.lua +++ /dev/null @@ -1,52 +0,0 @@ ---[[ --- --- This file is not required for your own configuration, --- but helps people determine if their system is setup correctly. --- ---]] - -local check_version = function() - local verstr = tostring(vim.version()) - if not vim.version.ge then - vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr)) - return - end - - if vim.version.ge(vim.version(), '0.11') then - vim.health.ok(string.format("Neovim version is: '%s'", verstr)) - else - vim.health.error(string.format("Neovim out of date: '%s'. Upgrade to latest stable or nightly", verstr)) - end -end - -local check_external_reqs = function() - -- Basic utils: `git`, `make`, `unzip` - for _, exe in ipairs { 'git', 'make', 'unzip', 'rg' } do - local is_executable = vim.fn.executable(exe) == 1 - if is_executable then - vim.health.ok(string.format("Found executable: '%s'", exe)) - else - vim.health.warn(string.format("Could not find executable: '%s'", exe)) - end - end - - return true -end - -return { - check = function() - vim.health.start 'kickstart.nvim' - - vim.health.info [[NOTE: Not every warning is a 'must-fix' in `:checkhealth` - - Fix only warnings for plugins and languages you intend to use. - Mason will give warnings for languages that are not installed. - You do not need to install, unless you want to use those languages!]] - - local uv = vim.uv or vim.loop - vim.health.info('System Information: ' .. vim.inspect(uv.os_uname())) - - check_version() - check_external_reqs() - end, -} diff --git a/lua/kickstart/plugins/debug.lua b/lua/kickstart/plugins/debug.lua deleted file mode 100644 index 7e58905e830..00000000000 --- a/lua/kickstart/plugins/debug.lua +++ /dev/null @@ -1,110 +0,0 @@ --- debug.lua --- --- Shows how to use the DAP plugin to debug your code. --- --- Primarily focused on configuring the debugger for Go, but can --- be extended to other languages as well. That's why it's called --- kickstart.nvim and not kitchen-sink.nvim ;) - ----@module 'lazy' ----@type LazySpec -return { - -- NOTE: Yes, you can install new plugins here! - 'mfussenegger/nvim-dap', - -- NOTE: And you can specify dependencies as well - dependencies = { - -- Creates a beautiful debugger UI - 'rcarriga/nvim-dap-ui', - - -- Required dependency for nvim-dap-ui - 'nvim-neotest/nvim-nio', - - -- Installs the debug adapters for you - 'mason-org/mason.nvim', - 'jay-babu/mason-nvim-dap.nvim', - - -- Add your own debuggers here - 'leoluz/nvim-dap-go', - }, - keys = { - -- Basic debugging keymaps, feel free to change to your liking! - { '', function() require('dap').continue() end, desc = 'Debug: Start/Continue' }, - { '', function() require('dap').step_into() end, desc = 'Debug: Step Into' }, - { '', function() require('dap').step_over() end, desc = 'Debug: Step Over' }, - { '', function() require('dap').step_out() end, desc = 'Debug: Step Out' }, - { 'b', function() require('dap').toggle_breakpoint() end, desc = 'Debug: Toggle Breakpoint' }, - { 'B', function() require('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. - { '', function() require('dapui').toggle() end, desc = 'Debug: See last session result.' }, - }, - config = function() - local dap = require 'dap' - local dapui = require 'dapui' - - require('mason-nvim-dap').setup { - -- Makes a best effort to setup the various debuggers with - -- reasonable debug configurations - automatic_installation = true, - - -- You can provide additional configuration to the handlers, - -- see mason-nvim-dap README for more information - handlers = {}, - - -- You'll need to check that you have the required things installed - -- online, please don't ask me how to install them :) - ensure_installed = { - -- Update this to ensure that you have the debuggers for the langs you want - 'delve', - }, - } - - -- Dap UI setup - -- For more information, see |:help nvim-dap-ui| - ---@diagnostic disable-next-line: missing-fields - 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. - icons = { expanded = '▾', collapsed = '▸', current_frame = '*' }, - ---@diagnostic disable-next-line: missing-fields - controls = { - icons = { - pause = '⏸', - play = '▶', - step_into = '⏎', - step_over = '⏭', - step_out = '⏮', - step_back = 'b', - run_last = '▶▶', - terminate = '⏹', - disconnect = '⏏', - }, - }, - } - - -- Change breakpoint icons - -- vim.api.nvim_set_hl(0, 'DapBreak', { fg = '#e51400' }) - -- vim.api.nvim_set_hl(0, 'DapStop', { fg = '#ffcc00' }) - -- local breakpoint_icons = vim.g.have_nerd_font - -- and { Breakpoint = '', BreakpointCondition = '', BreakpointRejected = '', LogPoint = '', Stopped = '' } - -- or { Breakpoint = '●', BreakpointCondition = '⊜', BreakpointRejected = '⊘', LogPoint = '◆', Stopped = '⭔' } - -- for type, icon in pairs(breakpoint_icons) do - -- local tp = 'Dap' .. type - -- local hl = (type == 'Stopped') and 'DapStop' or 'DapBreak' - -- vim.fn.sign_define(tp, { text = icon, texthl = hl, numhl = hl }) - -- end - - 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 - - -- Install golang specific config - require('dap-go').setup { - delve = { - -- On Windows delve must be run attached or it crashes. - -- See https://github.com/leoluz/nvim-dap-go/blob/main/README.md#configuring - detached = vim.fn.has 'win32' == 0, - }, - } - end, -} diff --git a/lua/kickstart/plugins/lint.lua b/lua/kickstart/plugins/lint.lua deleted file mode 100644 index 556f3178811..00000000000 --- a/lua/kickstart/plugins/lint.lua +++ /dev/null @@ -1,59 +0,0 @@ --- Linting - ----@module 'lazy' ----@type LazySpec -return { - 'mfussenegger/nvim-lint', - event = { 'BufReadPre', 'BufNewFile' }, - config = function() - local lint = require 'lint' - lint.linters_by_ft = { - markdown = { 'markdownlint' }, -- Make sure to install `markdownlint` via mason / npm - } - - -- To allow other plugins to add linters to require('lint').linters_by_ft, - -- instead set linters_by_ft like this: - -- lint.linters_by_ft = lint.linters_by_ft or {} - -- lint.linters_by_ft['markdown'] = { 'markdownlint' } - -- - -- However, note that this will enable a set of default linters, - -- which will cause errors unless these tools are available: - -- { - -- clojure = { "clj-kondo" }, - -- dockerfile = { "hadolint" }, - -- inko = { "inko" }, - -- janet = { "janet" }, - -- json = { "jsonlint" }, - -- markdown = { "vale" }, - -- rst = { "vale" }, - -- ruby = { "ruby" }, - -- terraform = { "tflint" }, - -- text = { "vale" } - -- } - -- - -- You can disable the default linters by setting their filetypes to nil: - -- lint.linters_by_ft['clojure'] = nil - -- lint.linters_by_ft['dockerfile'] = nil - -- lint.linters_by_ft['inko'] = nil - -- lint.linters_by_ft['janet'] = nil - -- lint.linters_by_ft['json'] = nil - -- lint.linters_by_ft['markdown'] = nil - -- lint.linters_by_ft['rst'] = nil - -- lint.linters_by_ft['ruby'] = nil - -- lint.linters_by_ft['terraform'] = nil - -- lint.linters_by_ft['text'] = nil - - -- Create autocommand which carries out the actual linting - -- on the specified events. - local lint_augroup = vim.api.nvim_create_augroup('lint', { clear = true }) - vim.api.nvim_create_autocmd({ 'BufEnter', 'BufWritePost', 'InsertLeave' }, { - group = lint_augroup, - callback = function() - -- Only run the linter in buffers that you can modify in order to - -- avoid superfluous noise, notably within the handy LSP pop-ups that - -- describe the hovered symbol using Markdown. - if vim.bo.modifiable then lint.try_lint() end - end, - }) - end, -}