diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
deleted file mode 100644
index 2ad4d31ddb0..00000000000
--- a/.github/ISSUE_TEMPLATE/bug_report.md
+++ /dev/null
@@ -1,28 +0,0 @@
----
-name: Bug report
-about: Create a report to help us improve
-title: ''
-labels: ''
-assignees: ''
-
----
-
-
-
-## Describe the bug
-
-
-## To Reproduce
-
-1. ...
-
-## Desktop
-
-- OS:
-- Terminal:
-
-## Neovim Version
-
-
-```
-```
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 00000000000..dc8f9dc9fe3
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -0,0 +1,24 @@
+name: Neovim config
+
+on:
+ push:
+ pull_request:
+
+permissions:
+ contents: read
+
+jobs:
+ smoke-test:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 15
+ steps:
+ - uses: actions/checkout@v6
+ - name: Install system dependencies
+ run: sudo apt-get update && sudo apt-get install -y build-essential curl git ripgrep
+ - name: Install Neovim 0.12.2
+ run: |
+ curl -fL https://github.com/neovim/neovim/releases/download/v0.12.2/nvim-linux-x86_64.tar.gz -o /tmp/nvim.tar.gz
+ tar -xzf /tmp/nvim.tar.gz -C /tmp
+ echo "/tmp/nvim-linux-x86_64/bin" >> "$GITHUB_PATH"
+ - name: Restore locked plugins and start Neovim
+ run: scripts/nvim-test.sh restore
diff --git a/.github/workflows/stylua.yml b/.github/workflows/stylua.yml
deleted file mode 100644
index 75db6c3355b..00000000000
--- a/.github/workflows/stylua.yml
+++ /dev/null
@@ -1,21 +0,0 @@
-# Check Lua Formatting
-name: Check Lua Formatting
-on: pull_request_target
-
-jobs:
- stylua-check:
- if: github.repository == 'nvim-lua/kickstart.nvim'
- name: Stylua Check
- runs-on: ubuntu-latest
- steps:
- - name: Checkout Code
- uses: actions/checkout@v2
- with:
- ref: ${{ github.event.pull_request.head.sha }}
- - name: Stylua Check
- uses: JohnnyMorganz/stylua-action@v3
- with:
- token: ${{ secrets.GITHUB_TOKEN }}
- version: latest
- args: --check .
-
diff --git a/.github/workflows/update-plugins.yml b/.github/workflows/update-plugins.yml
new file mode 100644
index 00000000000..a54f2fd832e
--- /dev/null
+++ b/.github/workflows/update-plugins.yml
@@ -0,0 +1,52 @@
+name: Update locked plugins
+
+on:
+ schedule:
+ - cron: '17 7 * * 1'
+ workflow_dispatch:
+
+permissions:
+ contents: write
+ pull-requests: write
+
+jobs:
+ update:
+ runs-on: ubuntu-24.04
+ timeout-minutes: 20
+ steps:
+ - uses: actions/checkout@v6
+ with:
+ fetch-depth: 0
+ - name: Install system dependencies
+ run: sudo apt-get update && sudo apt-get install -y build-essential curl git ripgrep
+ - name: Install Neovim 0.12.2
+ run: |
+ curl -fL https://github.com/neovim/neovim/releases/download/v0.12.2/nvim-linux-x86_64.tar.gz -o /tmp/nvim.tar.gz
+ tar -xzf /tmp/nvim.tar.gz -C /tmp
+ echo "/tmp/nvim-linux-x86_64/bin" >> "$GITHUB_PATH"
+ - name: Update plugins and smoke test the result
+ run: scripts/nvim-test.sh update
+ - name: Open or refresh the update pull request
+ env:
+ GH_TOKEN: ${{ github.token }}
+ run: |
+ if git diff --quiet -- lazy-lock.json; then
+ echo "Plugin lockfile is already current."
+ exit 0
+ fi
+
+ branch=automation/plugin-updates
+ git config user.name github-actions[bot]
+ git config user.email 41898282+github-actions[bot]@users.noreply.github.com
+ git fetch origin "$branch" || true
+ git switch -C "$branch"
+ git add lazy-lock.json
+ git commit -m "chore: update locked plugins"
+ git push --force-with-lease origin "$branch"
+
+ gh pr create \
+ --base "${GITHUB_REF_NAME}" \
+ --head "$branch" \
+ --title "chore: update locked Neovim plugins" \
+ --body "Automated weekly plugin update. Merge only after the Neovim config check passes." \
+ || gh pr edit "$branch" --body "Automated weekly plugin update. Merge only after the Neovim config check passes."
diff --git a/.gitignore b/.gitignore
index 005b535b606..f7ba19154cb 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,5 @@ test.sh
nvim
spell/
-lazy-lock.json
+# The plugin lockfile is intentionally committed. It is the rollback point for
+# safe updates and must not be ignored.
diff --git a/.nvim-version b/.nvim-version
new file mode 100644
index 00000000000..60d68b2300b
--- /dev/null
+++ b/.nvim-version
@@ -0,0 +1 @@
+v0.12.2
diff --git a/README.md b/README.md
index b6d19f49787..de239a73469 100644
--- a/README.md
+++ b/README.md
@@ -1,218 +1,50 @@
-# kickstart.nvim
+# River's Neovim config
-## Introduction
+This configuration targets **Neovim 0.12.x** and pins the complete plugin graph
+in `lazy-lock.json`. Normal editor startup never updates plugins. A weekly GitHub
+Actions job proposes lockfile updates in a pull request, and the same isolated
+smoke test runs on every push and pull request.
-A starting point for Neovim that is:
+That gives updates a rollback point and keeps upstream breaking changes out of
+the working editor until they pass CI. It cannot make arbitrary upstream
+changes risk-free, but it turns them into reviewed, reversible changes instead
+of surprise startup failures.
-* Small
-* Single-file
-* Completely Documented
+## Install
-**NOT** a Neovim distribution, but instead a starting point for your configuration.
-
-## Installation
-
-### Install Neovim
-
-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.
-If you are experiencing issues, please make sure you have the latest versions.
-
-### Install External Dependencies
-
-> **NOTE**
-> [Backup](#FAQ) your previous configuration (if any exists)
-
-External Requirements:
-- Basic utils: `git`, `make`, `unzip`, C Compiler (`gcc`)
-- [ripgrep](https://github.com/BurntSushi/ripgrep#installation)
-- Language Setup:
- - If want to write Typescript, you need `npm`
- - If want to write Golang, you will need `go`
- - etc.
-
-> **NOTE**
-> See [Windows Installation](#Windows-Installation) to double check any additional Windows notes
-
-Neovim's configurations are located under the following paths, depending on your OS:
-
-| OS | PATH |
-| :- | :--- |
-| Linux, MacOS | `$XDG_CONFIG_HOME/nvim`, `~/.config/nvim` |
-| Windows (cmd)| `%userprofile%\AppData\Local\nvim\` |
-| Windows (powershell)| `$env:USERPROFILE\AppData\Local\nvim\` |
-
-### Install Kickstart
-
-Clone kickstart.nvim:
-
- 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 %userprofile%\AppData\Local\nvim\
-```
-
-If you're using `powershell.exe`
-
-```
-git clone https://github.com/nvim-lua/kickstart.nvim.git $env:USERPROFILE\AppData\Local\nvim\
-```
-
-
-
-### Post Installation
-
-Start Neovim
+Required: Neovim 0.12.x, Git, a C compiler, `make`, `unzip`, and `ripgrep`. A Nerd
+Font is recommended. Language servers, formatters, and debuggers are installed
+through Mason on first interactive startup.
```sh
+git clone https://github.com/RiverMatsumoto/kickstart.nvim.git \
+ "${XDG_CONFIG_HOME:-$HOME/.config}/nvim"
nvim
```
-That's it! Lazy will install all the plugins you have. Use `:Lazy` to view
-current plugin status.
-
-Read through the `init.lua` file in your configuration folder for more
-information about extending and exploring Neovim.
-
-### Getting Started
-
-See [Effective Neovim: Instant IDE](https://youtu.be/stqUbv-5u2s), covering the
-previous version. Note: The install via init.lua is outdated, please follow the
-install instructions in this file instead. An updated video is coming soon.
-
-### Recommended Steps
-
-[Fork](https://docs.github.com/en/get-started/quickstart/fork-a-repo) this repo
-(so that you have your own copy that you can modify) and then installing you
-can install to your machine using the methods above.
-
-> **NOTE**
-> Your fork's url will be something like this: `https://github.com//kickstart.nvim.git`
-
-#### Examples of adding popularly requested plugins
-
-NOTE: You'll need to uncomment the line in the init.lua that turns on loading custom plugins.
-
-
- Adding autopairs
-
-This will automatically install [windwp/nvim-autopairs](https://github.com/windwp/nvim-autopairs) and enable it on startup. For more information, see documentation for [lazy.nvim](https://github.com/folke/lazy.nvim).
-
-In the file: `lua/custom/plugins/autopairs.lua`, add:
-
-```lua
--- File: lua/custom/plugins/autopairs.lua
-
-return {
- "windwp/nvim-autopairs",
- -- Optional dependency
- dependencies = { 'hrsh7th/nvim-cmp' },
- config = function()
- require("nvim-autopairs").setup {}
- -- If you want to automatically add `(` after selecting a function or method
- local cmp_autopairs = require('nvim-autopairs.completion.cmp')
- local cmp = require('cmp')
- cmp.event:on(
- 'confirm_done',
- cmp_autopairs.on_confirm_done()
- )
- end,
-}
-```
-
-
-
- Adding a file tree plugin
+To try it without replacing another config:
-This will install the tree plugin and add the command `:Neotree` for you. You can explore the documentation at [neo-tree.nvim](https://github.com/nvim-neo-tree/neo-tree.nvim) for more information.
-
-In the file: `lua/custom/plugins/filetree.lua`, add:
-
-```lua
--- Unless you are still migrating, remove the deprecated commands from v1.x
-vim.cmd([[ let g:neo_tree_remove_legacy_commands = 1 ]])
-
-return {
- "nvim-neo-tree/neo-tree.nvim",
- version = "*",
- dependencies = {
- "nvim-lua/plenary.nvim",
- "nvim-tree/nvim-web-devicons", -- not strictly required, but recommended
- "MunifTanjim/nui.nvim",
- },
- config = function ()
- require('neo-tree').setup {}
- end,
-}
-```
-
-
-
-### FAQ
-
-* What should I do if I already have a pre-existing neovim configuration?
- * You should back it up, then delete all files associated with it.
- * 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://github.com/folke/lazy.nvim#-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 `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 exact
- 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)
-
-### Windows Installation
-
-Installation may require installing build tools, and updating the run command for `telescope-fzf-native`
-
-See `telescope-fzf-native` documentation for [more details](https://github.com/nvim-telescope/telescope-fzf-native.nvim#installation)
-
-This requires:
-
-- Install CMake, and the Microsoft C++ Build Tools on Windows
-
-```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' }
+```sh
+git clone https://github.com/RiverMatsumoto/kickstart.nvim.git ~/.config/nvim-river
+NVIM_APPNAME=nvim-river nvim
```
-Alternatively one can install gcc and make which don't require changing the config,
-the easiest way is to use choco:
+## Updates
-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
-```
-
-2. install all requirements using choco, exit previous cmd and
-open a new one so that choco path is set, run in cmd as **admin**:
-```
-choco install -y neovim git ripgrep wget fd unzip gzip mingw make
-```
+- Do not use `:Lazy update` on the main branch for routine updates.
+- Merge the automated `chore: update locked Neovim plugins` pull request after
+ its checks pass.
+- To test an update locally, run `scripts/nvim-test.sh update`. This uses
+ isolated data/cache directories and changes only `lazy-lock.json`.
+- Restore the committed versions at any time with `:Lazy restore`.
-Then continue with the [Install Kickstart](#Install-Kickstart) step.
+The Neovim version is pinned in `.nvim-version`. Upgrade Neovim separately from
+plugin updates so failures have one clear cause.
+## Design choices
+The version guard, modular setup conventions, native `vim.lsp.config` API, and
+current Treesitter `main` API follow the useful compatibility patterns in
+[jdhao/nvim-config](https://github.com/jdhao/nvim-config). The system package
+installer is intentionally not run from Neovim; editor startup should never ask
+for administrator privileges or mutate the operating system.
diff --git a/init.lua b/init.lua
index 292ec0779a9..81872dc77cc 100644
--- a/init.lua
+++ b/init.lua
@@ -1,511 +1,627 @@
---[[
+vim.g.maplocalleader = ' '
+vim.g.mapleader = ' '
-=====================================================================
-==================== READ THIS BEFORE CONTINUING ====================
-=====================================================================
-======== .-----. ========
-======== .----------------------. | === | ========
-======== |.-""""""""""""""""""-.| |-----| ========
-======== || || | === | ========
-======== || KICKSTART.NVIM || |-----| ========
-======== || || | === | ========
-======== || || |-----| ========
-======== ||:Tutor || |:::::| ========
-======== |'-..................-'| |____o| ========
-======== `"")----------------(""` ___________ ========
-======== /::::::::::| |::::::::::\ \ no mouse \ ========
-======== /:::========| |==hjkl==:::\ \ required \ ========
-======== '""""""""""""' '""""""""""""' '""""""""""' ========
-======== ========
-=====================================================================
-=====================================================================
+vim.loader.enable()
-What is Kickstart?
+local version = vim.version()
+if version.major ~= 0 or version.minor ~= 12 then
+ error(('This config supports Neovim 0.12.x (found %s)'):format(tostring(version)))
+end
- Kickstart.nvim is *not* a distribution.
+-- =========================
+-- Lazy.nvim bootstrap
+-- =========================
+local lazypath = vim.fn.stdpath 'data' .. '/lazy/lazy.nvim'
+if not vim.uv.fs_stat(lazypath) then
+ local result = vim.system({
+ 'git',
+ 'clone',
+ '--filter=blob:none',
+ 'https://github.com/folke/lazy.nvim.git',
+ '--branch=stable',
+ lazypath,
+ }, { text = true }):wait()
+ if result.code ~= 0 then
+ error(('Unable to install lazy.nvim:\n%s'):format(result.stderr or 'unknown error'))
+ end
+end
+vim.opt.rtp:prepend(lazypath)
- 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.
+-- =========================
+-- PLUGINS
+-- =========================
+require('lazy').setup({
+ 'voldikss/vim-floaterm',
- 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!
+ 'tpope/vim-fugitive',
- 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/
+ 'rhysd/conflict-marker.vim',
- 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
+ { 'kylechui/nvim-surround', config = true },
-Kickstart Guide:
+ { 'numToStr/Comment.nvim', opts = {} },
- TODO: The very first thing you should do is to run the command `:Tutor` in Neovim.
+ {
+ 'karb94/neoscroll.nvim',
+ opts = {},
+ config = function()
+ local neoscroll = require 'neoscroll'
+
+ neoscroll.setup {
+ -- remove / from defaults so your custom maps win
+ mappings = {
+ '',
+ '',
+ '',
+ '',
+ 'zt',
+ 'zz',
+ 'zb',
+ },
+ hide_cursor = true, -- Hide cursor while scrolling
+ stop_eof = true, -- Stop at when scrolling downwards
+ respect_scrolloff = true, -- Stop scrolling when the cursor reaches the scrolloff margin of the file
+ cursor_scrolls_alone = true, -- The cursor will keep on scrolling even if the window cannot scroll further
+ duration_multiplier = 0.3, -- Global duration multiplier
+ easing = 'quadratic', -- Default easing function
+ pre_hook = function(info)
+ if info == 'center' then
+ vim.cmd 'normal! M'
+ end
+ end,
+ post_hook = nil,
+ }
+ local function near_file_edge()
+ local line = vim.api.nvim_win_get_cursor(0)[1]
+ local last = vim.api.nvim_buf_line_count(0)
+ local so = vim.wo.scrolloff
+ return line <= so + 1 or line >= last - so
+ end
- If you don't know what this means, type the following:
- -
- - :
- - Tutor
- -
+ local modes = { 'n', 'v', 'x' }
+
+ vim.keymap.set(modes, '', function()
+ neoscroll.ctrl_d { duration = 300 }
+ if not near_file_edge() then
+ vim.cmd 'normal! M'
+ end
+ end)
+
+ vim.keymap.set(modes, '', function()
+ neoscroll.ctrl_u { duration = 300 }
+ if not near_file_edge() then
+ vim.cmd 'normal! M'
+ end
+ end)
+ end,
+ },
- (If you already know how the Neovim basics, you can skip this step)
+ {
+ 'nvim-treesitter/nvim-treesitter',
+ branch = 'main',
+ lazy = false,
+ build = ':TSUpdate',
+ },
- Once you've completed that, you can continue working through **AND READING** the rest
- of the kickstart init.lua
+ {
+ 'MeanderingProgrammer/treesitter-modules.nvim',
+ dependencies = { 'nvim-treesitter/nvim-treesitter' },
+ config = function()
+ local parsers = {
+ 'bash',
+ 'c',
+ 'c_sharp',
+ 'cpp',
+ 'gdscript',
+ 'javascript',
+ 'json',
+ 'lua',
+ 'markdown',
+ 'python',
+ 'rust',
+ 'toml',
+ 'typescript',
+ 'yaml',
+ }
- 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.
+ require('treesitter-modules').setup {
+ ensure_installed = vim.env.NVIM_SKIP_TOOL_INSTALL == '1' and {} or parsers,
+ highlight = { enable = true },
+ incremental_selection = {
+ enable = true,
+ keymaps = {
+ init_selection = '',
+ node_incremental = '',
+ node_decremental = '',
+ scope_incremental = '',
+ },
+ },
+ }
+ end,
+ },
- 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.
+ 'rhysd/git-messenger.vim',
- MOST IMPORTANTLY, we provide a keymap "sh" to [s]earch the [h]elp documentation,
- which is very useful when you're not sure exactly what you're looking for.
+ {
+ 'lewis6991/gitsigns.nvim',
+ event = 'VeryLazy',
+ config = function()
+ require('gitsigns').setup {
+ signs = {
+ add = { text = '│' },
+ change = { text = '│' },
+ delete = { text = '_' },
+ topdelete = { text = '‾' },
+ changedelete = { text = '~' },
+ untracked = { text = '┆' },
+ },
+ }
+ end,
+ },
- 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.
+ {
+ 'sindrets/diffview.nvim',
+ event = 'VeryLazy',
+ dependencies = { 'nvim-lua/plenary.nvim' },
+ },
- NOTE: Look for lines like this
+ {
+ 'ErickKramer/git-coauthors.nvim',
+ dependencies = { 'nvim-telescope/telescope.nvim' },
+ },
- Throughout the file. These are for you, the reader, to help 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 nvim config.
+ -- Editor UX
+ {
+ 'windwp/nvim-autopairs',
+ config = function()
+ require('nvim-autopairs').setup {}
+ end,
+ },
-If you experience any errors while trying to install kickstart, run `:checkhealth` for more info
+ {
+ 'iamcco/markdown-preview.nvim',
+ build = function()
+ vim.fn['mkdp#util#install']()
+ end,
+ config = function()
+ vim.g.mkdp_refresh_slow = 1
+ vim.g.mkdp_page_title = '「${name}」'
+ vim.g.mkdp_theme = 'dark'
+ vim.g.mkdp_auto_close = 0
+ vim.g.mkdp_combine_preview = 1
+ vim.g.mkdp_combine_preview_auto_refresh = 1
+ end,
+ },
-I hope you enjoy your Neovim journey,
-- TJ
+ {
+ 'sphamba/smear-cursor.nvim',
+ opts = {
+ time_interval = 3,
+ cursor_color = '#ff4000',
+ particles_enabled = true,
+ stiffness = 0.5,
+ trailing_stiffness = 0.2,
+ trailing_exponent = 5,
+ damping = 0.6,
+ gradient_exponent = 0,
+ gamma = 1,
+ never_draw_over_target = true,
+ hide_target_hack = true,
+ particle_spread = 1,
+ particles_per_second = 500,
+ particles_per_length = 50,
+ particle_max_lifetime = 800,
+ particle_max_initial_velocity = 20,
+ particle_velocity_from_cursor = 0.5,
+ particle_damping = 0.15,
+ particle_gravity = -50,
+ min_distance_emit_particles = 0,
+ },
+ },
-P.S. You can delete this when you're done too. It's your config now! :)
---]]
+ {
+ 'f4z3r/gruvbox-material.nvim',
+ name = 'gruvbox-material',
+ lazy = false,
+ priority = 1000,
+ opts = {},
+ },
--- Set as the leader key
--- See `:help mapleader`
--- NOTE: Must happen before plugins are loaded (otherwise wrong leader will be used)
-vim.g.mapleader = ' '
-vim.g.maplocalleader = ' '
+ 'mg979/vim-visual-multi',
--- [[ Setting options ]]
--- See `:help vim.opt`
--- NOTE: You can change these options as you wish!
--- For more options, you can see `:help option-list`
+ -- File tree
+ {
+ 'nvim-neo-tree/neo-tree.nvim',
+ branch = 'v3.x',
+ dependencies = {
+ 'nvim-lua/plenary.nvim',
+ 'nvim-tree/nvim-web-devicons',
+ 'MunifTanjim/nui.nvim',
+ },
+ opts = {
+ enable_preview = true,
+
+ filesystem = {
+ -- open images (and anything else) externally
+ commands = {
+ system_open = function(state)
+ local node = state.tree:get_node()
+ local path = node:get_id() -- absolute path
+ vim.fn.jobstart({ 'xdg-open', path }, { detach = true })
+ end,
+ },
+ window = {
+ mappings = {
+ ['P'] = 'toggle_preview',
+ ['O'] = 'system_open', -- press O to open with external viewer
+ },
+ },
--- Make line numbers default
-vim.opt.number = true
--- You can also add relative line numbers, for help with jumping.
--- Experiment for yourself to see if you like it!
--- vim.opt.relativenumber = true
+ filtered_items = {
+ visible = false,
+ hide_dotfiles = true,
+ hide_gitignored = true,
+ hide_by_name = { '.git', '.elc' },
+ hide_by_pattern = { '*.uid', 'node_modules' },
+ },
+ },
+ },
+ },
--- Enable mouse mode, can be useful for resizing splits for example!
-vim.opt.mouse = 'a'
+ -- LAZY PLUGIN SPEC (replace bufferline with barbar)
+ {
+ 'romgrk/barbar.nvim',
+ dependencies = {
+ 'nvim-tree/nvim-web-devicons',
+ 'lewis6991/gitsigns.nvim', -- optional, for git status icons
+ },
+ init = function()
+ vim.g.barbar_auto_setup = false
+ end,
+ opts = {
+ animation = true,
+ auto_hide = false,
+ tabpages = false, -- show buffers, not Vim tabpages
+ clickable = true,
+ icons = {
+ preset = 'slanted',
+ button = '',
+ },
--- Don't show the mode, since it's already in status line
-vim.opt.showmode = false
+ -- Treat neo-tree as a sidebar so it doesn't become a "tab" and layout stays sane
+ sidebar_filetypes = {
+ ['neo-tree'] = { event = 'BufWipeout' },
+ },
+ },
+ },
--- Sync clipboard between OS and Neovim.
--- Remove this option if you want your OS clipboard to remain independent.
--- See `:help 'clipboard'`
-vim.opt.clipboard = 'unnamedplus'
+ 'nvim-tree/nvim-web-devicons',
--- Enable break indent
-vim.opt.breakindent = true
+ {
+ 'folke/todo-comments.nvim',
+ dependencies = 'nvim-lua/plenary.nvim',
+ opts = {},
+ },
--- Save undo history
-vim.opt.undofile = true
+ -- Flash (treesitter features removed)
+ {
+ 'folke/flash.nvim',
+ event = 'VeryLazy',
+ opts = {},
+ keys = {
+ {
+ 'r',
+ mode = 'o',
+ function()
+ require('flash').remote()
+ end,
+ desc = 'Remote Flash',
+ },
+ -- NOTE: removed: flash.treesitter(), flash.treesitter_search()
+ },
+ },
--- Case-insensitive searching UNLESS \C or capital in search
-vim.opt.ignorecase = true
-vim.opt.smartcase = true
+ {
+ 'folke/trouble.nvim',
+ dependencies = 'nvim-tree/nvim-web-devicons',
+ config = function()
+ require('trouble').setup {
+ signs = {
+ error = '',
+ warning = '',
+ hint = '',
+ information = '',
+ other = '',
+ },
+ }
+ end,
+ },
--- Keep signcolumn on by default
-vim.opt.signcolumn = 'yes'
+ { 'folke/which-key.nvim', opts = {} },
--- Decrease update time
-vim.opt.updatetime = 250
-vim.opt.timeoutlen = 300
+ {
+ 'nvim-lualine/lualine.nvim',
+ config = function()
+ local function get_venv()
+ local venv = vim.env.VIRTUAL_ENV
+ if venv then
+ local env = string.match(venv, '[^/]+$')
+ return ' ' .. env
+ end
+ return ''
+ end
--- Configure how new splits should be opened
-vim.opt.splitright = true
-vim.opt.splitbelow = true
+ require('lualine').setup {
+ options = {
+ icons_enabled = true,
+ theme = 'gruvbox-material',
+ component_separators = '|',
+ section_separators = '',
+ ignore_focus = {
+ 'dapui_watches',
+ 'dapui_breakpoints',
+ 'dapui_scopes',
+ 'dapui_console',
+ 'dapui_stacks',
+ 'dap-repl',
+ },
+ disabled_filetypes = { 'NvimTree' },
+ },
+ sections = {
+ lualine_a = { 'mode' },
+ lualine_b = { 'branch', 'diff', 'diagnostics' },
+ lualine_c = { 'filename' },
+ lualine_x = { { get_venv }, 'fileformat', 'filetype' },
+ lualine_y = { 'progress' },
+ lualine_z = { 'location' },
+ },
+ }
+ end,
+ },
--- Sets how neovim will display certain whitespace in the editor.
--- See `:help 'list'`
--- and `:help 'listchars'`
-vim.opt.list = true
-vim.opt.listchars = { tab = '» ', trail = '·', nbsp = '␣' }
+ { 'nvim-pack/nvim-spectre', opts = {} },
--- Preview substitutions live, as you type!
-vim.opt.inccommand = 'split'
+ {
+ 'lukas-reineke/indent-blankline.nvim',
+ main = 'ibl',
+ opts = {},
+ config = function()
+ local highlight = {
+ 'RainbowRed',
+ 'RainbowYellow',
+ 'RainbowBlue',
+ 'RainbowOrange',
+ 'RainbowGreen',
+ 'RainbowViolet',
+ 'RainbowCyan',
+ }
--- Show which line your cursor is on
-vim.opt.cursorline = true
+ local hooks = require 'ibl.hooks'
+ hooks.register(hooks.type.HIGHLIGHT_SETUP, function()
+ vim.api.nvim_set_hl(0, 'RainbowRed', { fg = '#E06C75' })
+ vim.api.nvim_set_hl(0, 'RainbowYellow', { fg = '#E5C07B' })
+ vim.api.nvim_set_hl(0, 'RainbowBlue', { fg = '#61AFEF' })
+ vim.api.nvim_set_hl(0, 'RainbowOrange', { fg = '#D19A66' })
+ vim.api.nvim_set_hl(0, 'RainbowGreen', { fg = '#98C379' })
+ vim.api.nvim_set_hl(0, 'RainbowViolet', { fg = '#C678DD' })
+ vim.api.nvim_set_hl(0, 'RainbowCyan', { fg = '#56B6C2' })
+ end)
+
+ require('ibl').setup {
+ indent = { highlight = highlight, char = '┊' },
+ scope = { enabled = false },
+ }
+ end,
+ },
--- Minimal number of screen lines to keep above and below the cursor.
-vim.opt.scrolloff = 10
-
--- [[ Basic Keymaps ]]
--- See `:help vim.keymap.set()`
-
--- Set highlight on search, but clear on pressing in normal mode
-vim.opt.hlsearch = true
-vim.keymap.set('n', '', 'nohlsearch')
-
--- Diagnostic keymaps
-vim.keymap.set('n', '[d', vim.diagnostic.goto_prev, { desc = 'Go to previous [D]iagnostic message' })
-vim.keymap.set('n', ']d', vim.diagnostic.goto_next, { desc = 'Go to next [D]iagnostic message' })
-vim.keymap.set('n', 'e', vim.diagnostic.open_float, { desc = 'Show diagnostic [E]rror messages' })
-vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Open diagnostic [Q]uickfix list' })
-
--- 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
--- is not what someone will guess without a bit more experience.
---
--- NOTE: This won't work in all terminal emulators/tmux/etc. Try your own mapping
--- or just use to exit terminal mode
-vim.keymap.set('t', '', '', { desc = 'Exit terminal mode' })
-
--- TIP: Disable arrow keys in normal mode
--- vim.keymap.set('n', '', 'echo "Use h to move!!"')
--- vim.keymap.set('n', '', 'echo "Use l to move!!"')
--- vim.keymap.set('n', '', 'echo "Use k to move!!"')
--- vim.keymap.set('n', '', 'echo "Use j to move!!"')
-
--- Keybinds to make split navigation easier.
--- Use CTRL+ to switch between windows
---
--- 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' })
-
--- [[ Basic Autocommands ]]
--- See `:help lua-guide-autocommands`
-
--- Highlight when yanking (copying) text
--- Try it with `yap` in normal mode
--- See `:help vim.highlight.on_yank()`
-vim.api.nvim_create_autocmd('TextYankPost', {
- desc = 'Highlight when yanking (copying) text',
- group = vim.api.nvim_create_augroup('kickstart-highlight-yank', { clear = true }),
- callback = function()
- vim.highlight.on_yank()
- end,
-})
+ -- Terminal UX
+ {
+ 'akinsho/toggleterm.nvim',
+ opts = {},
+ config = function()
+ require('toggleterm').setup {
+ size = 20,
+ open_mapping = [[]],
+ hide_numbers = true,
+ shade_filetypes = {},
+ shade_terminals = true,
+ shading_factor = 2,
+ start_in_insert = true,
+ insert_mappings = true,
+ persist_size = true,
+ direction = 'float',
+ close_on_exit = true,
+ shell = vim.o.shell,
+ float_opts = {
+ border = 'curved',
+ winblend = 0,
+ highlights = { border = 'Normal', background = 'Normal' },
+ },
+ }
--- [[ 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'
-if not vim.loop.fs_stat(lazypath) then
- local lazyrepo = 'https://github.com/folke/lazy.nvim.git'
- vim.fn.system { 'git', 'clone', '--filter=blob:none', '--branch=stable', lazyrepo, lazypath }
-end ---@diagnostic disable-next-line: undefined-field
-vim.opt.rtp:prepend(lazypath)
+ function _G.set_terminal_keymaps()
+ local opts = { noremap = true }
+ vim.diagnostic.enable(false, { bufnr = 0 })
+ vim.api.nvim_buf_set_keymap(0, 't', '', [[]], opts)
+ end
+ vim.cmd 'autocmd! TermOpen term://* lua set_terminal_keymaps()'
+
+ local Terminal = require('toggleterm.terminal').Terminal
+
+ local lazygit = Terminal:new {
+ cmd = 'lazygit',
+ hidden = true,
+ direction = 'float',
+ float_opts = { width = vim.o.columns, height = vim.o.lines },
+ on_open = function(term)
+ vim.cmd 'startinsert!'
+ vim.diagnostic.enable(false, { bufnr = 0 })
+ vim.api.nvim_buf_set_keymap(0, 't', '', 'close', { silent = false, noremap = true })
+ if vim.fn.mapcheck('', 't') ~= '' then
+ vim.api.nvim_buf_del_keymap(term.bufnr, 't', '')
+ end
+ end,
+ }
--- [[ 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 with a link (or for a github repo: 'owner/repo' link).
- 'tpope/vim-sleuth', -- Detect tabstop and shiftwidth automatically
-
- -- NOTE: Plugins can also be added by using a table,
- -- with the first argument being the link and the following
- -- keys can be used to configure plugin behavior/loading/etc.
- --
- -- Use `opts = {}` to force a plugin to be loaded.
- --
- -- This is equivalent to:
- -- require('Comment').setup({})
-
- -- "gc" to comment visual regions/lines
- { 'numToStr/Comment.nvim', opts = {} },
+ function _G._lazygit_toggle()
+ lazygit.dir = vim.fn.expand '%:p:h'
+ lazygit:toggle()
+ end
- -- Here is a more advanced example where we pass configuration
- -- options to `gitsigns.nvim`. This is equivalent to the following lua:
- -- require('gitsigns').setup({ ... })
- --
- -- 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',
- opts = {
- signs = {
- add = { text = '+' },
- change = { text = '~' },
- delete = { text = '_' },
- topdelete = { text = '‾' },
- changedelete = { text = '~' },
- },
- },
+ local python = Terminal:new {
+ cmd = 'ipython3',
+ direction = 'horizontal',
+ hidden = true,
+ hidden_numbers = true,
+ }
+ function _G._python_toggle()
+ python:toggle()
+ end
+
+ vim.api.nvim_set_keymap('n', 'lg', 'lua _lazygit_toggle()', { noremap = true, silent = true })
+ vim.api.nvim_set_keymap('n', 'ip', 'lua _python_toggle()', { noremap = true, silent = true })
+ end,
},
- -- 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 `config` key, the configuration only runs
- -- after the plugin has been loaded:
- -- config = function() ... end
-
- { -- Useful plugin to show you pending keybinds.
- 'folke/which-key.nvim',
- event = 'VimEnter', -- Sets the loading event to 'VimEnter'
- config = function() -- This is the function that runs, AFTER loading
- require('which-key').setup()
-
- -- Document existing key chains
- require('which-key').register {
- ['c'] = { name = '[C]ode', _ = 'which_key_ignore' },
- ['d'] = { name = '[D]ocument', _ = 'which_key_ignore' },
- ['r'] = { name = '[R]ename', _ = 'which_key_ignore' },
- ['s'] = { name = '[S]earch', _ = 'which_key_ignore' },
- ['w'] = { name = '[W]orkspace', _ = 'which_key_ignore' },
+ {
+ 'stevearc/dressing.nvim',
+ opts = {},
+ config = function()
+ require('dressing').setup {
+ input = {
+ get_config = function()
+ return {
+ title_pos = 'center',
+ win_options = { sidescrolloff = 10 },
+ insert_only = false,
+ }
+ end,
+ },
}
end,
},
- -- 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
+ { 'navarasu/onedark.nvim', priority = 1000 },
- { -- Fuzzy Finder (files, lsp, etc)
+ -- Telescope
+ {
'nvim-telescope/telescope.nvim',
- event = 'VimEnter',
- branch = '0.1.x',
+ branch = 'master',
dependencies = {
'nvim-lua/plenary.nvim',
- { -- If encountering errors, see telescope-fzf-native README for install 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 special font.
- -- If you already have a Nerd Font, or terminal set up with fallback fonts
- -- you can enable this
- -- { 'nvim-tree/nvim-web-devicons' }
+ 'benfowler/telescope-luasnip.nvim',
+ 'nvim-telescope/telescope-live-grep-args.nvim',
},
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()`
+ local actions = require 'telescope.actions'
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 = {}
+ defaults = {
+ prompt_prefix = '🔍 ',
+ vimgrep_arguments = {
+ 'rg',
+ '--color=never',
+ '--no-heading',
+ '--with-filename',
+ '--line-number',
+ '--column',
+ '--smart-case',
+ '--follow',
+ },
+ path_display = { truncate = 3 },
+ mappings = {
+ i = {
+ [''] = actions.select_tab,
+ [''] = actions.select_vertical,
+ [''] = actions.select_horizontal,
+ },
+ n = {
+ [''] = actions.select_tab,
+ [''] = actions.select_vertical,
+ [''] = actions.select_horizontal,
+ },
+ },
+ },
+ pickers = {
+ find_files = {
+ find_command = { 'rg', '--files', '--hidden', '-g', '!.git' },
+ follow = true,
+ },
+ lsp_document_symbols = { show_line = true },
+ },
extensions = {
- ['ui-select'] = {
- require('telescope.themes').get_dropdown(),
+ fzf = {
+ fuzzy = true,
+ override_generic_sorter = true,
+ override_file_sorter = true,
+ case_mode = 'smart_case',
},
},
}
-
- -- 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' })
- vim.keymap.set('n', 'sf', builtin.find_files, { desc = '[S]earch [F]iles' })
- vim.keymap.set('n', 'ss', builtin.builtin, { desc = '[S]earch [S]elect Telescope' })
- vim.keymap.set('n', 'sw', builtin.grep_string, { desc = '[S]earch current [W]ord' })
- vim.keymap.set('n', 'sg', builtin.live_grep, { desc = '[S]earch by [G]rep' })
- vim.keymap.set('n', 'sd', builtin.diagnostics, { desc = '[S]earch [D]iagnostics' })
- vim.keymap.set('n', 'sr', builtin.resume, { desc = '[S]earch [R]esume' })
- vim.keymap.set('n', 's.', builtin.oldfiles, { desc = '[S]earch Recent Files ("." for repeat)' })
- vim.keymap.set('n', '', builtin.buffers, { desc = '[ ] Find existing buffers' })
-
- -- Slightly advanced example of overriding default behavior and theme
- vim.keymap.set('n', '/', function()
- -- You can pass additional configuration to telescope to change theme, layout, etc.
- builtin.current_buffer_fuzzy_find(require('telescope.themes').get_dropdown {
- winblend = 10,
- previewer = false,
- })
- end, { desc = '[/] Fuzzily search in current buffer' })
-
- -- Also possible to pass additional configuration options.
- -- See `:help telescope.builtin.live_grep()` for information about particular keys
- vim.keymap.set('n', 's/', function()
- builtin.live_grep {
- grep_open_files = true,
- prompt_title = 'Live Grep in Open Files',
- }
- end, { 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' })
+ require('telescope').load_extension 'fzf'
+ require('telescope').load_extension 'luasnip'
+ require('telescope').load_extension 'live_grep_args'
end,
},
- { -- LSP Configuration & Plugins
+ -- LSP
+ {
'neovim/nvim-lspconfig',
dependencies = {
- -- Automatically install LSPs and related tools to stdpath for neovim
- 'williamboman/mason.nvim',
- 'williamboman/mason-lspconfig.nvim',
+ { 'mason-org/mason.nvim', config = true },
+ 'mason-org/mason-lspconfig.nvim',
'WhoIsSethDaniel/mason-tool-installer.nvim',
-
- -- Useful status updates for LSP.
- -- NOTE: `opts = {}` is the same as calling `require('fidget').setup({})`
- { 'j-hui/fidget.nvim', opts = {} },
+ {
+ 'j-hui/fidget.nvim',
+ config = function()
+ require('fidget').setup {
+ notification = {
+ window = {
+ normal_hl = 'Comment',
+ border = 'rounded',
+ zindex = 45,
+ max_width = 0,
+ max_height = 0,
+ x_padding = 1,
+ y_padding = 0,
+ align = 'bottom',
+ relative = 'editor',
+ },
+ },
+ }
+ end,
+ },
},
config = function()
- -- Brief Aside: **What is LSP?**
- --
- -- LSP is an acronym 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 }),
+ group = vim.api.nvim_create_augroup('personal-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
- -- many times.
- --
- -- 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)
vim.keymap.set('n', keys, func, { buffer = event.buf, desc = 'LSP: ' .. desc })
end
-
- -- 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 .
map('gd', require('telescope.builtin').lsp_definitions, '[G]oto [D]efinition')
-
- -- Find references for the word under your cursor.
map('gr', require('telescope.builtin').lsp_references, '[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.
map('gI', require('telescope.builtin').lsp_implementations, '[G]oto [I]mplementation')
-
- -- 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*.
map('D', require('telescope.builtin').lsp_type_definitions, 'Type [D]efinition')
-
- -- Fuzzy find all the symbols in your current document.
- -- Symbols are things like variables, functions, types, etc.
map('ds', require('telescope.builtin').lsp_document_symbols, '[D]ocument [S]ymbols')
-
- -- Fuzzy find all the symbols in your current workspace
- -- Similar to document symbols, except searches over your whole project.
map('ws', require('telescope.builtin').lsp_dynamic_workspace_symbols, '[W]orkspace [S]ymbols')
-
- -- Rename the variable under your cursor
- -- Most Language Servers support renaming across files, etc.
map('rn', vim.lsp.buf.rename, '[R]e[n]ame')
-
- -- 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('ca', vim.lsp.buf.code_action, '[C]ode [A]ction')
+ map('T', vim.lsp.buf.hover, 'Hover Documentation')
+ map('', vim.lsp.buf.signature_help, 'Signature Documentation')
- -- Opens a popup that displays documentation about the word under your cursor
- -- See `:help K` for why this keymap
- map('K', vim.lsp.buf.hover, 'Hover Documentation')
-
- -- WARN: This is not Goto Definition, this is Goto Declaration.
- -- For example, in C this would take you to the header
- map('gD', vim.lsp.buf.declaration, '[G]oto [D]eclaration')
-
- -- 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.server_capabilities.documentHighlightProvider then
vim.api.nvim_create_autocmd({ 'CursorHold', 'CursorHoldI' }, {
buffer = event.buf,
callback = vim.lsp.buf.document_highlight,
})
-
vim.api.nvim_create_autocmd({ 'CursorMoved', 'CursorMovedI' }, {
buffer = event.buf,
callback = vim.lsp.buf.clear_references,
@@ -514,151 +630,90 @@ require('lazy').setup {
end,
})
- -- LSP servers and clients are able to communicate to each other what features they support.
- -- By default, Neovim doesn't support everything that is in the LSP Specification.
- -- When you add nvim-cmp, luasnip, etc. Neovim now has *more* capabilities.
- -- So, we create new capabilities with nvim cmp, and then broadcast that to the servers.
- local capabilities = vim.lsp.protocol.make_client_capabilities()
- capabilities = vim.tbl_deep_extend('force', capabilities, require('cmp_nvim_lsp').default_capabilities())
-
- -- Enable the following language servers
- -- Feel free to add/remove any LSPs that you want here. They will automatically be installed.
- --
- -- Add any additional override configuration in the following tables. Available keys are:
- -- - cmd (table): Override the default command used to start the server
- -- - filetypes (table): Override the default list of associated filetypes for the server
- -- - capabilities (table): Override fields in capabilities. Can be used to disable certain LSP features.
- -- - settings (table): Override the default settings passed when initializing the server.
- -- For example, to see the options for `lua_ls`, you could go to: https://luals.github.io/wiki/settings/
local servers = {
- -- clangd = {},
- -- gopls = {},
- -- pyright = {},
- -- rust_analyzer = {},
- -- ... etc. See `:help lspconfig-all` for a list of all the pre-configured LSPs
- --
- -- 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 (`tsserver`) will work just fine
- -- tsserver = {},
- --
-
+ clangd = {
+ cmd = {
+ 'clangd',
+ '--background-index',
+ '--clang-tidy',
+ '--completion-style=bundled',
+ '--cross-file-rename',
+ '--header-insertion=iwyu',
+ },
+ },
+ pyright = {
+ settings = {
+ python = {
+ analysis = {
+ autoSearchPaths = true,
+ diagnosticMode = 'openFilesOnly',
+ useLibraryCodeForTypes = true,
+ reportDuplicateImport = true,
+ },
+ },
+ },
+ },
lua_ls = {
- -- cmd = {...},
- -- filetypes { ...},
- -- capabilities = {},
settings = {
Lua = {
runtime = { version = 'LuaJIT' },
workspace = {
checkThirdParty = false,
- -- Tells lua_ls where to find all the Lua files that you have loaded
- -- for your neovim configuration.
- library = {
- '${3rd}/luv/library',
- unpack(vim.api.nvim_get_runtime_file('', true)),
- },
- -- If lua_ls is really slow on your computer, you can try this instead:
- -- library = { vim.env.VIMRUNTIME },
+ library = { '${3rd}/luv/library', unpack(vim.api.nvim_get_runtime_file('', true)) },
},
- completion = {
- callSnippet = 'Replace',
- },
- -- You can toggle below to ignore Lua_LS's noisy `missing-fields` warnings
- -- diagnostics = { disable = { 'missing-fields' } },
+ completion = { callSnippet = 'Replace' },
+ diagnostics = { globals = { 'vim', 'require' } },
},
},
},
+ rust_analyzer = {},
}
- -- 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
- require('mason').setup()
-
- -- You can add other tools here that you want Mason to install
- -- for you, so that they are available from within Neovim.
- local ensure_installed = vim.tbl_keys(servers or {})
- vim.list_extend(ensure_installed, {
- 'stylua', -- Used to format lua code
- })
- require('mason-tool-installer').setup { ensure_installed = ensure_installed }
+ local capabilities = vim.lsp.protocol.make_client_capabilities()
+ capabilities = require('cmp_nvim_lsp').default_capabilities(capabilities)
+ capabilities.offsetEncoding = { 'utf-16' }
+ capabilities.textDocument.completion.completionItem.snippetSupport = true
+ capabilities.textDocument.foldingRange = { dynamicRegistration = false, lineFoldingOnly = true }
+
+ require('mason').setup { ui = { border = 'rounded' } }
+ for server_name, server in pairs(servers) do
+ server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {})
+ vim.lsp.config(server_name, server)
+ end
require('mason-lspconfig').setup {
- handlers = {
- function(server_name)
- local server = servers[server_name] or {}
- -- This handles overriding only values explicitly passed
- -- by the server configuration above. Useful when disabling
- -- certain features of an LSP (for example, turning off formatting for tsserver)
- server.capabilities = vim.tbl_deep_extend('force', {}, capabilities, server.capabilities or {})
- require('lspconfig')[server_name].setup(server)
- end,
- },
+ ensure_installed = vim.env.NVIM_SKIP_TOOL_INSTALL == '1' and {} or vim.tbl_keys(servers),
+ automatic_enable = true,
}
- end,
- },
- { -- Autoformat
- 'stevearc/conform.nvim',
- opts = {
- notify_on_error = false,
- format_on_save = {
- timeout_ms = 500,
- lsp_fallback = true,
- },
- formatters_by_ft = {
- lua = { 'stylua' },
- -- Conform can also run multiple formatters sequentially
- -- python = { "isort", "black" },
- --
- -- You can use a sub-list to tell conform to run *until* a formatter
- -- is found.
- -- javascript = { { "prettierd", "prettier" } },
- },
- },
- },
-
- { -- Autocompletion
- 'hrsh7th/nvim-cmp',
- event = 'InsertEnter',
- dependencies = {
- -- Snippet Engine & its associated nvim-cmp source
- {
- 'L3MON4D3/LuaSnip',
- 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)(),
- },
- 'saadparwaiz1/cmp_luasnip',
-
- -- Adds other completion capabilities.
- -- nvim-cmp does not ship with all sources by default. They are split
- -- into multiple repos for maintenance purposes.
- 'hrsh7th/cmp-nvim-lsp',
- 'hrsh7th/cmp-path',
+ require('mason-tool-installer').setup {
+ ensure_installed = {
+ 'clangd',
+ 'clang-format',
+ 'lua_ls',
+ 'pyright',
+ 'ruff',
+ 'codelldb',
+ 'cpptools',
+ 'cpplint',
+ 'csharpier',
+ 'csharp-language-server',
+ 'netcoredbg',
+ 'omnisharp',
+ 'omnisharp-mono',
+ 'rust-analyzer',
+ 'sonarlint-language-server',
+ 'stylua',
+ 'prettier',
+ },
+ auto_update = false,
+ run_on_start = vim.env.NVIM_SKIP_TOOL_INSTALL ~= '1',
+ start_delay = 3000,
+ debounce_hours = 5,
+ }
- -- If you want to add a bunch of pre-configured snippets,
- -- you can use this plugin to help you. It even has snippets
- -- for various frameworks/libraries/etc. but you will have to
- -- set up the ones that are useful for you.
- -- 'rafamadriz/friendly-snippets',
- },
- config = function()
- -- See `:help cmp`
local cmp = require 'cmp'
local luasnip = require 'luasnip'
- luasnip.config.setup {}
cmp.setup {
snippet = {
@@ -666,156 +721,978 @@ require('lazy').setup {
luasnip.lsp_expand(args.body)
end,
},
- completion = { completeopt = 'menu,menuone,noinsert' },
-
- -- For an understanding of why these mappings were
- -- chosen, you will need to read `:help ins-completion`
- --
- -- No, but seriously. Please read `:help ins-completion`, it is really good!
mapping = cmp.mapping.preset.insert {
- -- Select the [n]ext item
[''] = cmp.mapping.select_next_item(),
- -- Select the [p]revious item
[''] = cmp.mapping.select_prev_item(),
-
- -- Accept ([y]es) the completion.
- -- This will auto-import if your LSP supports it.
- -- This will expand snippets if the LSP sent a snippet.
- [''] = cmp.mapping.confirm { select = true },
-
- -- Manually trigger a completion from nvim-cmp.
- -- Generally you don't need this, because nvim-cmp will display
- -- completions whenever it has completion options available.
- [''] = cmp.mapping.complete {},
-
- -- Think of as moving to the right of your snippet expansion.
- -- So if you have a snippet that's like:
- -- function $name($args)
- -- $body
- -- end
- --
- -- will move you to the right of each of the expansion locations.
- -- is similar, except moving you backwards.
- [''] = cmp.mapping(function()
- if luasnip.expand_or_locally_jumpable() then
+ [''] = cmp.mapping.scroll_docs(-4),
+ [''] = cmp.mapping.scroll_docs(4),
+ [''] = cmp.mapping.complete(),
+ [''] = cmp.mapping.abort(),
+ [''] = cmp.mapping.confirm { behavior = cmp.ConfirmBehavior.Replace, select = true },
+ [''] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_next_item()
+ elseif luasnip.expand_or_jumpable() then
luasnip.expand_or_jump()
+ else
+ fallback()
end
end, { 'i', 's' }),
- [''] = cmp.mapping(function()
- if luasnip.locally_jumpable(-1) then
+ [''] = cmp.mapping(function(fallback)
+ if cmp.visible() then
+ cmp.select_prev_item()
+ elseif luasnip.jumpable(-1) then
luasnip.jump(-1)
+ else
+ fallback()
end
end, { 'i', 's' }),
},
sources = {
- { name = 'nvim_lsp' },
+ { name = 'nvim_lsp', max_item_count = 10 },
{ name = 'luasnip' },
- { name = 'path' },
+ { name = 'path', max_item_count = 5 },
+ { name = 'buffer', max_item_count = 5 },
+ },
+ window = {
+ completion = cmp.config.window.bordered(),
+ documentation = cmp.config.window.bordered(),
+ },
+ }
+
+ function _G.leave_snippet()
+ if
+ ((vim.v.event.old_mode == 's' and vim.v.event.new_mode == 'n') or vim.v.event.old_mode == 'i')
+ and require('luasnip').session.current_nodes[vim.api.nvim_get_current_buf()]
+ and not require('luasnip').session.jump_active
+ then
+ require('luasnip').unlink_current()
+ end
+ end
+ vim.api.nvim_command [[ autocmd ModeChanged * lua leave_snippet() ]]
+
+ local cmp_enabled = true
+ vim.api.nvim_create_user_command('ToggleAutoComplete', function()
+ if cmp_enabled then
+ require('cmp').setup.buffer { enabled = false }
+ cmp_enabled = false
+ else
+ require('cmp').setup.buffer { enabled = true }
+ cmp_enabled = true
+ end
+ end, {})
+
+ cmp.setup.cmdline('/', {
+ mapping = cmp.mapping.preset.cmdline(),
+ sources = { { name = 'buffer' } },
+ })
+ cmp.setup.cmdline(':', {
+ mapping = cmp.mapping.preset.cmdline(),
+ sources = cmp.config.sources({ { name = 'path' } }, {
+ { name = 'cmdline', option = { ignore_cmds = { 'Man', '!' } } },
+ }),
+ })
+
+ vim.diagnostic.config {
+ virtual_text = { prefix = '●' },
+ severity_sort = true,
+ float = { source = 'always' },
+ signs = {
+ text = {
+ [vim.diagnostic.severity.ERROR] = ' ',
+ [vim.diagnostic.severity.WARN] = ' ',
+ [vim.diagnostic.severity.HINT] = ' ',
+ [vim.diagnostic.severity.INFO] = ' ',
+ },
},
}
end,
},
- { -- 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',
- lazy = false, -- make sure we load this during startup if it is your main colorscheme
- priority = 1000, -- make sure to load this before all the other start plugins
+ -- Formatter
+ {
+ 'stevearc/conform.nvim',
config = function()
- -- Load the colorscheme here
- vim.cmd.colorscheme 'tokyonight-night'
+ require('conform').setup {
+ formatters_by_ft = {
+ json = { { 'prettierd', 'prettier' } },
+ lua = { 'stylua' },
+ markdown = { 'prettier' },
+ python = function(bufnr)
+ if require('conform').get_formatter_info('ruff_format', bufnr).available then
+ return { 'ruff_format' }
+ end
+ return { 'isort', 'black' }
+ end,
+ yaml = { 'prettier' },
+ rust = { 'rustfmt' },
+ ['*'] = { 'injected' },
+ },
+ ignore_errors = true,
+ -- NOTE: removed treesitter mapping; keep only what you need
+ lang_to_ext = {
+ bash = 'sh',
+ latex = 'tex',
+ markdown = 'md',
+ python = 'py',
+ },
+ }
- -- You can configure highlights by doing something like
- vim.cmd.hi 'Comment gui=none'
+ vim.api.nvim_create_user_command('Format', function(args)
+ local range = nil
+ if args.count ~= -1 then
+ local end_line = vim.api.nvim_buf_get_lines(0, args.line2 - 1, args.line2, true)[1]
+ range = {
+ start = { args.line1, 0 },
+ ['end'] = { args.line2, end_line:len() },
+ }
+ end
+ require('conform').format { async = true, lsp_format = 'fallback', range = range }
+ end, { range = true })
+
+ vim.keymap.set('', 'fa', function()
+ require('conform').format { async = true, lsp_format = 'fallback' }
+ end, { desc = '[F]ormat [a]ll' })
end,
},
- -- Highlight todo, notes, etc in comments
- { 'folke/todo-comments.nvim', event = 'VimEnter', dependencies = { 'nvim-lua/plenary.nvim' }, opts = { signs = false } },
+ -- Completion
+ {
+ 'hrsh7th/nvim-cmp',
+ dependencies = {
+ 'L3MON4D3/LuaSnip',
+ 'saadparwaiz1/cmp_luasnip',
+ 'hrsh7th/cmp-nvim-lsp',
+ 'hrsh7th/cmp-buffer',
+ 'hrsh7th/cmp-path',
+ 'hrsh7th/cmp-cmdline',
+ },
+ },
+ {
+ 'rafamadriz/friendly-snippets',
+ config = function()
+ require('luasnip.loaders.from_vscode').lazy_load()
+ require('luasnip/loaders/from_vscode').lazy_load {
+ paths = { vim.fn.stdpath 'config' .. '/snippets' },
+ }
+ end,
+ },
- { -- Collection of various small independent plugins/modules
- 'echasnovski/mini.nvim',
+ {
+ 'Kurama622/llm.nvim',
+ dependencies = { 'nvim-lua/plenary.nvim', 'MunifTanjim/nui.nvim' },
+ cmd = { 'LLMSessionToggle', 'LLMSelectedTextHandler', 'LLMAppHandler' },
config = function()
- -- Better Around/Inside textobjects
- --
- -- Examples:
- -- - va) - [V]isually select [A]round [)]paren
- -- - yinq - [Y]ank [I]nside [N]ext [']quote
- -- - 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 statusline = require 'mini.statusline'
- statusline.setup()
-
- -- You can configure sections in the statusline by overriding their
- -- default behavior. For example, here we disable the section for
- -- cursor information because line numbers are already enabled
- ---@diagnostic disable-next-line: duplicate-set-field
- statusline.section_location = function()
- return ''
- end
+ require('llm').setup {
+ -- GitHub Models (Azure inference)
+ url = 'https://api.openai.com/v1/chat/completions',
+ api_type = 'openai',
+
+ -- Set this to what you want to use (must support /chat/completions)
+ model = _G.chatgpt_model or 'gpt-5-nano',
+
+ -- Sensible coding defaults
+ --max_tokens = 4095,
+ --temperature = 0.2,
+ --top_p = 0.1,
+
+ -- Keep it code-focused
+ prompt = 'You are a helpful programming assistant. Be concise, show code when needed, and prefer practical fixes.',
+ -- Visual selection -> context prompt
+ selected_text_handler = {
+ prompt = function(selection)
+ return string.format(
+ 'You are a helpful programming assistant.\n\n' .. 'Context (selected text):\n' .. '```text\n%s\n```\n\n' .. 'Task:\n',
+ selection
+ )
+ end,
+ },
+
+ -- Optional cosmetics (minimal)
+ spinner = {
+ text = { '⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏' },
+ hl = 'Title',
+ },
+ prefix = {
+ user = { text = 'You: ', hl = 'Title' },
+ assistant = { text = 'AI: ', hl = 'Added' },
+ },
+
+ -- Persist chat history
+ save_session = true,
+ max_history = 30,
+ max_history_name_length = 40,
+
+ -- Keymaps inside the session UI
+ keys = {
+ -- Input window
+ ['Input:Submit'] = { mode = 'i', key = '' }, -- Enter sends (your issue)
+ ['Input:Cancel'] = { mode = { 'n', 'i' }, key = '' },
+ ['Input:Resend'] = { mode = { 'n', 'i' }, key = '' },
+
+ -- History (only if save_session=true)
+ ['Input:HistoryNext'] = { mode = { 'n', 'i' }, key = '' },
+ ['Input:HistoryPrev'] = { mode = { 'n', 'i' }, key = '' },
+
+ -- Output window ("split" style)
+ ['Output:Ask'] = { mode = 'n', key = 'i' },
+ ['Output:Cancel'] = { mode = 'n', key = '' },
+ ['Output:Resend'] = { mode = 'n', key = '' },
+
+ -- Session window ("float" style)
+ ['Session:Toggle'] = { mode = 'n', key = 'ai' }, -- your chosen toggle
+ ['Session:Close'] = { mode = 'n', key = { '', 'Q' } },
+ },
+
+ -- Optional diff display used by some tools/handlers
+ display = {
+ diff = {
+ layout = 'vertical',
+ opts = {
+ 'internal',
+ 'filler',
+ 'closeoff',
+ 'algorithm:patience',
+ 'followwrap',
+ 'linematch:120',
+ },
+ provider = 'mini_diff',
+ disable_diagnostic = true,
+ },
+ },
- -- ... and there is more!
- -- Check out: https://github.com/echasnovski/mini.nvim
+ callbacks = {
+ on_response = function(resp)
+ local usage = resp and resp.usage
+ if usage then
+ vim.notify(
+ string.format(
+ 'LLM tokens — prompt: %d, completion: %d, total: %d',
+ usage.prompt_tokens or 0,
+ usage.completion_tokens or 0,
+ usage.total_tokens or 0
+ ),
+ vim.log.levels.INFO
+ )
+ end
+ end,
+ },
+
+ -- If you later add tools:
+ app_handler = {
+ -- Visual-select text -> run this -> output in a floating result window.
+ AskWithContext = {
+ handler = 'attach_to_chat_handler',
+ prompt = [[
+ Answer the user’s question (likely code related) using the context very briefly and concisely:
+ ]],
+ opts = {
+ -- this is the key part: append the visual selection after the prompt
+ apply_visual_selection = true, -- described in docs for flexi_handler :contentReference[oaicite:2]{index=2}
+ is_codeblock = true,
+ enter_input = true,
+ enter_flexible_window = false,
+ --exit_on_move = true,
+ },
+ },
+ CodeExplain = {
+ handler = 'flexi_handler',
+ prompt = 'Explain the following code very briefly, please only return the explanation',
+ opts = {
+ enter_flexible_window = true,
+ },
+ },
+ },
+ }
end,
+ keys = {
+ { 'ai', 'LLMSessionToggle', mode = 'n', silent = true, desc = 'LLM: toggle session' },
+ { 'as', 'LLMSelectedTextHandler', mode = 'x', silent = true, desc = 'LLM: send selection' },
+ -- If you add tools:
+ { 'ax', 'LLMAppHandler CodeExplain', mode = 'x', silent = true, desc = 'LLM tool: Explain' },
+ { 'ai', 'LLMAppHandler AskWithContext', mode = 'x', silent = true, desc = 'LLM tool: Ask with context' },
+ },
},
- { -- Highlight, edit, and navigate code
- 'nvim-treesitter/nvim-treesitter',
- build = ':TSUpdate',
+ {
+ 'MeanderingProgrammer/render-markdown.nvim',
+ dependencies = {
+ 'nvim-treesitter/nvim-treesitter',
+ 'nvim-mini/mini.icons',
+ }, -- if you use standalone mini plugins
+ ft = { 'markdown', 'llm' },
+
+ config = function()
+ require('render-markdown').setup {
+ restart_highlighter = true,
+ heading = {
+ enabled = true,
+ sign = false,
+ position = 'overlay', -- inline | overlay
+ icons = { ' ', ' ', ' ', ' ', ' ', ' ' },
+ signs = { ' ' },
+ width = 'block',
+ left_margin = 0,
+ left_pad = 0,
+ right_pad = 0,
+ min_width = 0,
+ border = false,
+ border_virtual = false,
+ border_prefix = false,
+ above = '▄',
+ below = '▀',
+ backgrounds = {},
+ foregrounds = {
+ 'RenderMarkdownH1',
+ 'RenderMarkdownH2',
+ 'RenderMarkdownH3',
+ 'RenderMarkdownH4',
+ 'RenderMarkdownH5',
+ 'RenderMarkdownH6',
+ },
+ },
+ dash = {
+ enabled = true,
+ icon = '─',
+ width = 0.5,
+ left_margin = 0.5,
+ highlight = 'RenderMarkdownDash',
+ },
+ code = { style = 'normal' },
+ }
+ end,
+ },
+
+ -- DAP
+ {
+ 'mfussenegger/nvim-dap',
+ dependencies = {
+ 'mfussenegger/nvim-dap-python',
+ 'rcarriga/nvim-dap-ui',
+ 'theHamsta/nvim-dap-virtual-text',
+ 'nvim-neotest/nvim-nio',
+ 'folke/neodev.nvim',
+ },
config = function()
- -- [[ Configure Treesitter ]] See `:help nvim-treesitter`
+ require('neodev').setup { library = { plugins = { 'nvim-dap-ui' }, types = true } }
+
+ local dap = require 'dap'
+ local sign = vim.fn.sign_define
+
+ sign('DapBreakpoint', { text = ' ', texthl = 'DapBreakpoint', linehl = '', numhl = '' })
+ sign('DapBreakpointCondition', { text = ' ', texthl = 'DapBreakpointCondition', linehl = '', numhl = '' })
+ sign('DapLogPoint', { text = '◆ ', texthl = 'DapLogPoint', linehl = '', numhl = '' })
+ sign('DapStoppedLine', { text = ' ', texthl = 'DapLogPoint', linehl = '', numhl = '' })
+ sign('DapBreakpointRejected', { text = ' ', texthl = 'DapBreakpointRejected', linehl = '', numhl = '' })
+
+ dap.adapters.codelldb = {
+ type = 'server',
+ port = '${port}',
+ executable = {
+ command = vim.fn.expand '$HOME/.local/share/nvim/mason/bin/codelldb',
+ args = { '--port', '${port}' },
+ },
+ }
- ---@diagnostic disable-next-line: missing-fields
- require('nvim-treesitter.configs').setup {
- ensure_installed = { 'bash', 'c', 'html', 'lua', 'markdown', 'vim', 'vimdoc' },
- -- Autoinstall languages that are not installed
- auto_install = true,
- highlight = { enable = true },
- indent = { enable = true },
+ dap.configurations.cpp = {
+ {
+ name = 'C++: Run file',
+ type = 'codelldb',
+ request = 'launch',
+ program = function()
+ return vim.fn.input('Path to executable: ', vim.fn.getcwd() .. '/', 'file')
+ end,
+ cwd = '${workspaceFolder}',
+ stopOnEntry = false,
+ },
+ {
+ name = 'C++: Attach to process',
+ type = 'codelldb',
+ request = 'attach',
+ pid = require('dap.utils').pick_process,
+ args = {},
+ },
}
- -- There are additional nvim-treesitter modules that you can use to interact
- -- with nvim-treesitter. You should go explore a few and see what interests you:
- --
- -- - Incremental selection: Included, see `:help nvim-treesitter-incremental-selection-mod`
- -- - Show your current context: https://github.com/nvim-treesitter/nvim-treesitter-context
- -- - Treesitter + textobjects: https://github.com/nvim-treesitter/nvim-treesitter-textobjects
+ require('dapui').setup {
+ controls = { icons = { pause = '⏸ ', play = '▶ ', terminate = '⏹ ' } },
+ floating = { border = 'rounded' },
+ layouts = {
+ {
+ elements = {
+ { id = 'stacks', size = 0.30 },
+ { id = 'breakpoints', size = 0.20 },
+ { id = 'scopes', size = 0.50 },
+ },
+ position = 'left',
+ size = 40,
+ },
+ {
+ elements = { { id = 'console', size = 0.50 }, { id = 'repl', size = 0.50 } },
+ position = 'bottom',
+ size = 10,
+ },
+ },
+ }
+
+ require('nvim-dap-virtual-text').setup()
+ require('dap-python').setup()
+ require('dap-python').test_runner = 'pytest'
+
+ vim.api.nvim_create_user_command('DapUIToggle', ":lua require('dapui').toggle()", {})
+ vim.api.nvim_create_user_command('DapPytestMethod', ":lua require('dap-python').test_method()", {})
+ vim.api.nvim_create_user_command('DapResetUI', ":lua require('dapui').open({reset = true})", { desc = 'Reset DAP UI Layout' })
end,
},
+}, {
+ lockfile = vim.fn.stdpath 'config' .. '/lazy-lock.json',
+ checker = { enabled = false },
+ change_detection = { enabled = false, notify = false },
+ install = { colorscheme = { 'gruvbox-material', 'habamax' } },
+})
+
+-- =========================================
+-- ============ START SMEAR PROFILE ========
+-- =========================================
+local smear = require 'smear_cursor'
+
+local smear_profiles = {
+ silver_blade = {
+ -- General
+ cursor_color = '#ffe6b2',
+ smear_between_buffers = true,
+ smear_between_neighbor_lines = true,
+ min_horizontal_distance_smear = 0,
+ min_vertical_distance_smear = 0,
+ smear_horizontally = true,
+ smear_vertically = true,
+ smear_diagonally = true,
+ smear_to_cmd = true,
+ scroll_buffer_space = true,
+
+ legacy_computing_symbols_support = false,
+ legacy_computing_symbols_support_vertical_bars = false,
+ use_diagonal_blocks = true,
+
+ vertical_bar_cursor = false,
+ smear_insert_mode = true,
+ vertical_bar_cursor_insert_mode = true,
+ smear_replace_mode = false,
+ smear_terminal_mode = false,
+ horizontal_bar_cursor_replace_mode = true,
+
+ never_draw_over_target = false,
+ hide_target_hack = false,
+
+ max_kept_windows = 50,
+ windows_zindex = 300,
+ filetypes_disabled = {},
+
+ -- High FPS (smooth)
+ time_interval = 7,
+ delay_disable = nil,
+ delay_event_to_smear = 1,
+ delay_after_key = 6,
+
+ -- Physics: fast head, laggy tail, smooth decay
+ stiffness = 0.95,
+ trailing_stiffness = 0.33,
+ anticipation = 0.06,
+ damping = 0.90,
+ trailing_exponent = 5.5,
+ distance_stop_animating = 0.06,
+
+ -- Insert mode: match feel
+ stiffness_insert_mode = 0.95,
+ trailing_stiffness_insert_mode = 0.7,
+ damping_insert_mode = 0.92,
+ trailing_exponent_insert_mode = 5.5,
+ distance_stop_animating_vertical_bar = 0.25,
+
+ -- Diagonal + shading tuned for “pretty”
+ max_slope_horizontal = (1 / 3) / 1.7,
+ min_slope_vertical = 2 * 1.7,
+ max_angle_difference_diagonal = math.pi / 18,
+ max_offset_diagonal = 0.18,
+ min_shade_no_diagonal = 0.22,
+ min_shade_no_diagonal_vertical_bar = 0.55,
+
+ -- Rich blending (costly but nice)
+ color_levels = 24,
+ gamma = 2.2,
+ gradient_exponent = 2.8,
+ max_shade_no_matrix = 0.78,
+ matrix_pixel_threshold = 0.72,
+ matrix_pixel_threshold_vertical_bar = 0.28,
+ matrix_pixel_min_factor = 0.55,
+ volume_reduction_exponent = 0.22,
+ minimum_volume_factor = 0.78,
+
+ -- Longer trail
+ max_length = 34,
+ max_length_insert_mode = 2,
+
+ -- Particles off
+ particles_enabled = false,
+ particle_max_num = 100,
+ particle_spread = 0.5,
+ particles_per_second = 200,
+ particles_per_length = 1.0,
+ particle_max_lifetime = 300,
+ particle_lifetime_distribution_exponent = 5,
+ particle_max_initial_velocity = 10,
+ particle_velocity_from_cursor = 0.2,
+ particle_random_velocity = 100,
+ particle_damping = 0.2,
+ particle_gravity = 20,
+ min_distance_emit_particles = 1.5,
+ particle_switch_octant_braille = 0.3,
+ particles_over_text = false,
+ },
+ -- 1) Frost Mist
+ eco_smear = {
+ -- General (keep core behavior)
+ cursor_color = '#ffe6b2',
+ smear_between_buffers = true,
+ smear_between_neighbor_lines = true,
+ min_horizontal_distance_smear = 1, -- reduces tiny smears
+ min_vertical_distance_smear = 1,
+ smear_horizontally = true,
+ smear_vertically = true,
+ smear_diagonally = false, -- big CPU win
+ smear_to_cmd = true,
+ scroll_buffer_space = false, -- cheaper on scroll
+
+ legacy_computing_symbols_support = false,
+ legacy_computing_symbols_support_vertical_bars = false,
+ use_diagonal_blocks = true,
+
+ vertical_bar_cursor = false,
+ smear_insert_mode = true,
+ vertical_bar_cursor_insert_mode = true,
+ smear_replace_mode = false,
+ smear_terminal_mode = false,
+ horizontal_bar_cursor_replace_mode = true,
+
+ never_draw_over_target = false,
+ hide_target_hack = false,
+
+ max_kept_windows = 20, -- fewer render windows kept
+ windows_zindex = 300,
+ filetypes_disabled = {},
+
+ -- Lower FPS (much cheaper)
+ time_interval = 14,
+ delay_disable = nil,
+ delay_event_to_smear = 2,
+ delay_after_key = 10,
+
+ -- Physics: still fast head, shorter/cheaper tail
+ stiffness = 0.90,
+ trailing_stiffness = 0.28,
+ anticipation = 0.04,
+ damping = 0.88,
+ trailing_exponent = 3.0,
+ distance_stop_animating = 0.12,
+
+ -- Insert mode: keep it tight
+ stiffness_insert_mode = 0.90,
+ trailing_stiffness_insert_mode = 0.28,
+ damping_insert_mode = 0.90,
+ trailing_exponent_insert_mode = 3.0,
+ distance_stop_animating_vertical_bar = 0.30,
+
+ -- Simpler shading (cheaper)
+ max_slope_horizontal = (1 / 3) / 1.6,
+ min_slope_vertical = 2 * 1.6,
+ max_angle_difference_diagonal = math.pi / 16,
+ max_offset_diagonal = 0.2,
+ min_shade_no_diagonal = 0.30,
+ min_shade_no_diagonal_vertical_bar = 0.60,
+
+ -- Reduced blending cost
+ color_levels = 16,
+ gamma = 2.2,
+ gradient_exponent = 1.3,
+ max_shade_no_matrix = 0.80,
+ matrix_pixel_threshold = 0.80,
+ matrix_pixel_threshold_vertical_bar = 0.35,
+ matrix_pixel_min_factor = 0.65,
+ volume_reduction_exponent = 0.35,
+ minimum_volume_factor = 0.82,
+
+ -- Shorter trail
+ max_length = 18,
+ max_length_insert_mode = 1,
+
+ -- Particles off
+ particles_enabled = false,
+ particle_max_num = 100,
+ particle_spread = 0.5,
+ particles_per_second = 200,
+ particles_per_length = 1.0,
+ particle_max_lifetime = 300,
+ particle_lifetime_distribution_exponent = 5,
+ particle_max_initial_velocity = 10,
+ particle_velocity_from_cursor = 0.2,
+ particle_random_velocity = 100,
+ particle_damping = 0.2,
+ particle_gravity = 20,
+ min_distance_emit_particles = 1.5,
+ particle_switch_octant_braille = 0.3,
+ particles_over_text = false,
+ },
+}
- -- The following two 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
- -- put them in the right spots if you want.
-
- -- 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',
-
- -- 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.
- -- For additional information, see `:help lazy.nvim-lazy.nvim-structuring-your-plugins`
- -- { import = 'custom.plugins' },
+-- smear settings
+local smear_profile_order = {
+ 'silver_blade',
+ 'eco_smear',
}
--- The line beneath this is called `modeline`. See `:help modeline`
--- vim: ts=2 sts=2 sw=2 et
+local current_idx = 1
+
+local function apply_smear_profile(name)
+ local p = smear_profiles[name]
+ if not p then
+ vim.notify('Unknown smear profile: ' .. tostring(name), vim.log.levels.ERROR)
+ return
+ end
+ smear.setup(vim.deepcopy(p))
+ vim.g.smear_cursor_profile = name
+ vim.notify('smear-cursor → ' .. name)
+end
+
+-- Commands
+vim.api.nvim_create_user_command('SmearProfile', function(opts)
+ apply_smear_profile(opts.args)
+end, {
+ nargs = 1,
+ complete = function()
+ return smear_profile_order
+ end,
+})
+-- Apply a default on startup (pick one)
+apply_smear_profile 'silver_blade'
+
+vim.api.nvim_create_user_command('SmearProfileNext', function()
+ current_idx = (current_idx % #smear_profile_order) + 1
+ apply_smear_profile(smear_profile_order[current_idx])
+end, {})
+
+vim.api.nvim_create_user_command('SmearProfilePrev', function()
+ current_idx = ((current_idx - 2) % #smear_profile_order) + 1
+ apply_smear_profile(smear_profile_order[current_idx])
+end, {})
+
+-- Keybindings (edit if you want)
+vim.keymap.set('n', 'pn', 'SmearProfileNext', { desc = 'Smear profile: next' })
+vim.keymap.set('n', 'pp', 'SmearProfilePrev', { desc = 'Smear profile: prev' })
+
+-- =========================================
+-- ============ END SMEAR PROFILE ==========
+-- =========================================
+
+-- =========================
+-- Editor settings
+-- =========================
+vim.o.hlsearch = false
+vim.opt.nu = true
+vim.opt.relativenumber = true
+vim.o.mouse = 'a'
+vim.o.breakindent = true
+vim.o.undofile = true
+vim.o.ignorecase = true
+vim.o.smartcase = true
+vim.o.updatetime = 250
+vim.o.timeoutlen = 300
+vim.opt.splitright = true
+vim.opt.splitbelow = true
+vim.opt.inccommand = 'split'
+vim.wo.signcolumn = 'yes'
+vim.opt.signcolumn = 'yes:1'
+vim.opt.cursorline = true
+vim.opt.colorcolumn = '100'
+vim.opt.tabstop = 4
+vim.opt.shiftwidth = 4
+vim.opt.expandtab = true
+vim.opt.listchars = { trail = '⇲', tab = '◦ ' }
+vim.opt.list = true
+vim.opt.clipboard = 'unnamedplus'
+vim.o.completeopt = 'menuone,noselect'
+vim.keymap.set('n', 'k', "v:count == 0 ? 'gk' : 'k'", { expr = true, silent = true })
+vim.keymap.set('n', 'j', "v:count == 0 ? 'gj' : 'j'", { expr = true, silent = true })
+vim.opt.termguicolors = true
+
+-- ============================
+-- ====== TABSTOP START =======
+-- ============================
+local function set_indent(ts)
+ vim.opt_local.expandtab = true
+ vim.opt_local.tabstop = ts
+ vim.opt_local.shiftwidth = ts
+ vim.opt_local.softtabstop = ts
+end
+
+vim.api.nvim_create_autocmd('FileType', {
+ pattern = { 'lua', 'javascript', 'typescript', 'tsx', 'json', 'yaml', 'toml', 'html', 'css' },
+ callback = function()
+ set_indent(2)
+ end,
+})
+
+vim.api.nvim_create_autocmd('FileType', {
+ pattern = { 'python', 'sh', 'bash', 'zsh', 'go', 'rust', 'cpp', 'c' },
+ callback = function()
+ set_indent(4)
+ end,
+})
+
+-- Makefiles must use real tabs
+vim.api.nvim_create_autocmd('FileType', {
+ pattern = { 'make' },
+ callback = function()
+ vim.opt_local.expandtab = false
+ vim.opt_local.tabstop = 8
+ vim.opt_local.shiftwidth = 8
+ vim.opt_local.softtabstop = 0
+ end,
+})
+-- ============================
+-- ====== TABSTOP END =========
+-- ============================
+
+local highlight_group = vim.api.nvim_create_augroup('YankHighlight', { clear = true })
+vim.api.nvim_create_autocmd('TextYankPost', {
+ callback = function()
+ vim.highlight.on_yank()
+ end,
+ group = highlight_group,
+ pattern = '*',
+})
+
+local numtogGrp = vim.api.nvim_create_augroup('NumberToggle', { clear = true })
+vim.api.nvim_create_autocmd({ 'BufEnter', 'InsertLeave', 'FocusGained' }, {
+ pattern = '*',
+ callback = function()
+ vim.opt.relativenumber = true
+ end,
+ group = numtogGrp,
+})
+vim.api.nvim_create_autocmd({ 'BufLeave', 'InsertEnter', 'FocusLost' }, {
+ pattern = '*',
+ callback = function()
+ vim.opt.relativenumber = false
+ end,
+ group = numtogGrp,
+})
+
+-- =========================
+-- Keymaps (treesitter keymaps removed)
+-- =========================
+
+-- scrolling / search centering, disabled for neoscroll
+-- vim.keymap.set('n', '', 'zz', { desc = 'Scroll down and center cursor' })
+-- vim.keymap.set('n', '', 'zz', { desc = 'Scroll up and center cursor' })
+-- vim.keymap.set('n', 'n', 'nzzzv', { desc = 'Next search result and center' })
+-- vim.keymap.set('n', 'N', 'Nzzzv', { desc = 'Previous search result and center' })
+
+-- move selected lines
+vim.keymap.set('v', 'J', ":m '>+1gv=gv", { desc = 'Move selection down' })
+vim.keymap.set('v', 'K', ":m '<-2gv=gv", { desc = 'Move selection up' })
+
+-- window navigation
+vim.keymap.set('n', '', 'h', { desc = 'Go to left window' })
+vim.keymap.set('n', '', 'j', { desc = 'Go to lower window' })
+vim.keymap.set('n', '', 'k', { desc = 'Go to upper window' })
+vim.keymap.set('n', '', 'l', { desc = 'Go to right window' })
+
+-- clipboard / yank
+vim.keymap.set('x', 'p', [["_dP]], { desc = 'Paste without overwriting register' })
+vim.keymap.set('n', 'ya', ':%y+', { desc = 'Yank entire buffer to clipboard' })
+
+-- diagnostics
+vim.keymap.set('n', '[d', function()
+ vim.diagnostic.jump { count = -1, float = true }
+end, { desc = 'Previous diagnostic' })
+vim.keymap.set('n', ']d', function()
+ vim.diagnostic.jump { count = 1, float = true }
+end, { desc = 'Next diagnostic' })
+vim.keymap.set('n', 'e', vim.diagnostic.open_float, { desc = 'Show diagnostic under cursor' })
+vim.keymap.set('n', 'q', vim.diagnostic.setloclist, { desc = 'Diagnostics to loclist' })
+vim.keymap.set('n', 'dd', function()
+ vim.diagnostic.enable(false)
+end, { desc = 'Disable diagnostics' })
+vim.keymap.set('n', 'de', function()
+ vim.diagnostic.enable(true)
+end, { desc = 'Enable diagnostics' })
+
+-- misc
+vim.keymap.set('n', 'cw', ':cd %:p:h:pwd', { desc = 'cd to current file directory' })
+vim.keymap.set('n', '', '', { noremap = true, desc = 'Jump forward in jumplist' })
+vim.keymap.set('n', '', ':write', { desc = 'Save file' })
+vim.keymap.set('i', '', ':write', { desc = 'Save file' })
+vim.keymap.set('n', 'cd', ':ToggleAutoComplete', { desc = 'Toggle autocomplete' })
+vim.keymap.set('n', 'ce', ':ToggleAutoComplete', { desc = 'Toggle autocomplete (alias)' })
+
+-- Telescope
+vim.keymap.set('n', '?', require('telescope.builtin').oldfiles, { desc = 'Find recently opened files' })
+vim.keymap.set('n', '', require('telescope.builtin').buffers, { desc = 'List open buffers' })
+vim.keymap.set('n', '/', function()
+ require('telescope.builtin').current_buffer_fuzzy_find(require('telescope.themes').get_dropdown {
+ previewer = false,
+ sorting_strategy = 'ascending',
+ })
+end, { desc = 'Fuzzy search in current buffer' })
+
+vim.keymap.set('n', 'sf', require('telescope.builtin').find_files, { desc = 'Find files' })
+vim.keymap.set('n', 'sh', require('telescope.builtin').help_tags, { desc = 'Search help tags' })
+vim.keymap.set('n', 'sw', require('telescope.builtin').grep_string, { desc = 'Search word under cursor' })
+vim.keymap.set('n', 'sg', require('telescope.builtin').live_grep, { desc = 'Live grep' })
+vim.keymap.set('n', 'sd', require('telescope.builtin').diagnostics, { desc = 'Search diagnostics' })
+vim.keymap.set('n', 'sp', require('telescope.builtin').spell_suggest, { desc = 'Spell suggestions' })
+vim.keymap.set('n', 'sk', require('telescope.builtin').keymaps, { desc = 'Search keymaps' })
+vim.keymap.set('n', 'gf', require('telescope.builtin').git_files, { desc = 'Find git files' })
+vim.keymap.set('n', 'sc', require('telescope.builtin').git_commits, { desc = 'Search git commits' })
+vim.keymap.set('n', 'sr', require('telescope.builtin').resume, { desc = 'Resume last Telescope picker' })
+
+vim.keymap.set('n', 's/', function()
+ require('telescope.builtin').live_grep {
+ grep_open_files = true,
+ prompt_title = 'Live Grep in Open Files',
+ }
+end, { desc = 'Live grep in open files' })
+
+vim.keymap.set('n', 'sn', function()
+ require('telescope.builtin').find_files { cwd = vim.fn.stdpath 'config' }
+end, { desc = 'Search Neovim config files' })
+
+-- run / permissions
+vim.keymap.set('n', 'ru', ':w:!%:p', { desc = 'Save and run current file' })
+vim.keymap.set('n', 'me', ':!chmod +x %:p', { desc = 'Make file executable' })
+vim.keymap.set('n', 'P', require('spectre').open, { desc = 'Open Spectre search/replace' })
+
+-- Trouble
+vim.keymap.set('n', 'xx', 'Trouble diagnostics toggle', { silent = true, desc = 'Toggle Trouble diagnostics' })
+vim.keymap.set('n', 'xw', 'Trouble diagnostics toggle', { silent = true, desc = 'Workspace diagnostics (Trouble)' })
+vim.keymap.set('n', 'xd', 'Trouble diagnostics toggle filter.buf=0', { silent = true, desc = 'Document diagnostics (Trouble)' })
+
+-- DAP
+vim.keymap.set('n', 'dap', ":lua require('dapui').toggle()", { desc = 'Toggle DAP UI' })
+vim.keymap.set('n', 'dc', ":lua require('dap').continue()", { desc = 'DAP continue' })
+vim.keymap.set('n', 'do', ":lua require('dap').step_over()", { desc = 'DAP step over' })
+vim.keymap.set('n', 'di', ":lua require('dap').step_into()", { desc = 'DAP step into' })
+vim.keymap.set('n', 'dk', function()
+ require('dap.ui.widgets').hover()
+end, { desc = 'DAP hover value' })
+vim.keymap.set('n', 'd?', function()
+ local widgets = require 'dap.ui.widgets'
+ widgets.centered_float(widgets.scopes)
+end, { desc = 'DAP scopes' })
+vim.keymap.set('n', 'du', ":lua require('dap').step_out()", { desc = 'DAP step out' })
+vim.keymap.set('n', 'dl', ":lua require('dapui').float_element()", { silent = true, noremap = true, desc = 'DAP floating window' })
+vim.keymap.set('n', 'dt', ":lua require('dap').toggle_breakpoint()", { silent = true, noremap = true, desc = 'Toggle breakpoint' })
+vim.keymap.set('n', 'dm', ":lua require('dap-python').test_method()", { silent = true, noremap = true, desc = 'DAP test method' })
+vim.keymap.set('n', 'df', ":lua require('dap-python').test_class()", { silent = true, noremap = true, desc = 'DAP test class' })
+
+-- bufferline
+
+local opts = { noremap = true, silent = true }
+
+-- KEYMAPS (Barbar)
+local opts = { noremap = true, silent = true }
+
+vim.keymap.set('n', '', 'BufferGoto 1', vim.tbl_extend('force', opts, { desc = 'Go to buffer 1' }))
+vim.keymap.set('n', '', 'BufferGoto 2', vim.tbl_extend('force', opts, { desc = 'Go to buffer 2' }))
+vim.keymap.set('n', '', 'BufferGoto 3', vim.tbl_extend('force', opts, { desc = 'Go to buffer 3' }))
+vim.keymap.set('n', '', 'BufferGoto 4', vim.tbl_extend('force', opts, { desc = 'Go to buffer 4' }))
+vim.keymap.set('n', '', 'BufferGoto 5', vim.tbl_extend('force', opts, { desc = 'Go to buffer 5' }))
+vim.keymap.set('n', '', 'BufferGoto 6', vim.tbl_extend('force', opts, { desc = 'Go to buffer 6' }))
+vim.keymap.set('n', '', 'BufferGoto 7', vim.tbl_extend('force', opts, { desc = 'Go to buffer 7' }))
+vim.keymap.set('n', '