2025年1月27日 星期一

NVIM handbook

LSP document : https://neovim.io/doc/user/lsp.html

Bolck fold document : https://neovim.io/doc/user/fold.html

Regular expression pattern : https://neovim.io/doc/user/pattern.html

Lazyvim : https://www.lazyvim.org/configuration/lazy.nvim


 1. init.lua

if not use init.lua but use init.vim, try add

lua require("init") 

in init.vim file

2. Install font

https://www.nerdfonts.com/font-downloads

After download, install, and select font with "mono"

3. Change window size 

Use ctrl + <-, ->

There are several window commands that allow you to do this:

  • Ctrl+W +/-: increase/decrease height (ex. 20<C-w>+)
  • Ctrl+W >/<: increase/decrease width (ex. 30<C-w><)
  • Ctrl+W _: set height (ex. 50<C-w>_)
  • Ctrl+W |: set width (ex. 50<C-w>|)
  • Ctrl+W =: equalize width and height of all windows

See also: :help CTRL-W

https://vi.stackexchange.com/questions/514/how-do-i-change-the-current-splits-width-and-height

3.a. Block collapse/fold/open

https://neovim.io/doc/user/fold.html

Fold : zc

Open : zo

...

4. Change window

User 

ctrl + h,j,k,l

or

c + ww

5. Show hidden file in tree

H ( shift + h )

6. switch tab ( edit buffer ) in edit view

H, L (shift + h / shift + l)

7.  handle buffer

space + b + ...

8. install lsp

:LazyExtras

press 'x' to enable plugin lsp

one can use '/' to search, for example : /lang.go search for golang

9. Nevigate

g : show go to menu

gd : goto definition

gD : goto declaration

gc : comment lines

^o : back

9: #j : go to relative j line , #k : go to k relative line above

10 : >> : ident right, << : ident left 

11 : " ff" find in files , " fF" find in file of pwd

12 : ":Telescope live_grep" life grep

13 : check plugin function : :checkhealth telescope

14 : check keymap : :verbose map <leader>fg

15 : add live grep keyp map

15.a : reference to lazy/telescope.nvim/README.md, here we can use 
Normal : 
nnoremap <leader>fg <cmd>Telescope live_grep<cr>
Lua :
nnoremap <leader>fg <cmd>lua require('telescope.builtin').live_grep()<cr>

15.b : modify lazy/LazyVim/lua/lazyvim/plugins/extras/editor/telescope.lua, add a line
{ "<leader>fG", "<cmd>Telescope live_grep<cr>", desc = "Telescope live_grep" },

15.c : ^q : you will leave the find result in the split buffer below

15.d : multiple file replace : :cdo %s/xxx/yyy/gc

15.e : map "current cursor text" to search
{ "<leader>fs", "<cmd>Telescope grep_string<cr>", desc = "Telescope grep_string" },


16. Debug golang

To use nvim debug golang, we need 

1. delve

2. dap related modules

2.1 dap-core, nvim-dap, nvim-dap-ui, nvim-dap-go

2.2 after install, use <leader>d will enter debug mode

17. Grep string under cursor

      { "<leader>sw", LazyVim.pick("grep_string", { word_match = "-w" }), desc = "Word (Root Dir)" },

      { "<leader>sW", LazyVim.pick("grep_string", { root = false, word_match = "-w" }), desc = "Word (cwd)" },

      { "<leader>sw", LazyVim.pick("grep_string"), mode = "v", desc = "Selection (Root Dir)" },

      { "<leader>sW", LazyVim.pick("grep_string", { root = false }), mode = "v", desc = "Selection (cwd)" },

      { "<leader>uC", LazyVim.pick("colorscheme", { enable_preview = true }), desc = "Colorscheme with Preview" },


diff --git a/lua/lazyvim/plugins/extras/editor/telescope.lua b/lua/lazyvim/plugins/extras/editor/telescope.lua

index 55ccace..0806788 100644

--- a/lua/lazyvim/plugins/extras/editor/telescope.lua

+++ b/lua/lazyvim/plugins/extras/editor/telescope.lua

@@ -108,6 +108,8 @@ return {

       { "<leader>ff", LazyVim.pick("files"), desc = "Find Files (Root Dir)" },

       { "<leader>fF", LazyVim.pick("files", { root = false }), desc = "Find Files (cwd)" },

       { "<leader>fg", "<cmd>Telescope git_files<cr>", desc = "Find Files (git-files)" },

+      { "<leader>fG", "<cmd>Telescope live_grep<cr>", desc = "Telescope live_grep" },

+      { "<leader>fs", "<cmd>Telescope grep_string<cr>", desc = "Telescope grep_string" },

       { "<leader>fr", "<cmd>Telescope oldfiles<cr>", desc = "Recent" },

       { "<leader>fR", LazyVim.pick("oldfiles", { cwd = vim.uv.cwd() }), desc = "Recent (cwd)" },

       -- git

17. Formating

Json : :%!jq '.' , https://codegoalie.com/posts/format-json-nvim-jq/


18. Use ctags : golang

ctags -R --languages=Go .


19. Add a plugin

a. path is ~/.config/nvim/lua/plugins

b. put xxx.lua under it

c. example

-- since this is just an example spec, don't actually load anything here and return an empty spec

-- stylua: ignore

if true then return {} end


-- every spec file under the "plugins" directory will be loaded automatically by lazy.nvim

--

-- In your plugin files, you can:

-- * add extra plugins

-- * disable/enabled LazyVim plugins

-- * override the configuration of LazyVim plugins

return {

  -- add gruvbox

  { "ellisonleao/gruvbox.nvim" },


  -- Configure LazyVim to load gruvbox

  {

    "LazyVim/LazyVim",

    opts = {

      colorscheme = "gruvbox",

    },

  },


  -- change trouble config

  {

    "folke/trouble.nvim",

    -- opts will be merged with the parent spec

    opts = { use_diagnostic_signs = true },

  },


  -- disable trouble

  { "folke/trouble.nvim", enabled = false },


  -- override nvim-cmp and add cmp-emoji

  {

    "hrsh7th/nvim-cmp",

    dependencies = { "hrsh7th/cmp-emoji" },

    ---@param opts cmp.ConfigSchema

    opts = function(_, opts)

      table.insert(opts.sources, { name = "emoji" })

    end,

  },


  -- change some telescope options and a keymap to browse plugin files

  {

    "nvim-telescope/telescope.nvim",

    keys = {

      -- add a keymap to browse plugin files

      -- stylua: ignore

      {

        "<leader>fp",

        function() require("telescope.builtin").find_files({ cwd = require("lazy.core.config").options.root }) end,

        desc = "Find Plugin File",

      },

    },

    -- change some options

    opts = {

      defaults = {

        layout_strategy = "horizontal",

        layout_config = { prompt_position = "top" },

        sorting_strategy = "ascending",

        winblend = 0,

      },

    },

  },


  -- add pyright to lspconfig

  {

    "neovim/nvim-lspconfig",

    ---@class PluginLspOpts

    opts = {

      ---@type lspconfig.options

      servers = {

        -- pyright will be automatically installed with mason and loaded with lspconfig

        pyright = {},

      },

    },

  },


  -- add tsserver and setup with typescript.nvim instead of lspconfig

  {

    "neovim/nvim-lspconfig",

    dependencies = {

      "jose-elias-alvarez/typescript.nvim",

      init = function()

        require("lazyvim.util").lsp.on_attach(function(_, buffer)

          -- stylua: ignore

          vim.keymap.set( "n", "<leader>co", "TypescriptOrganizeImports", { buffer = buffer, desc = "Organize Imports" })

          vim.keymap.set("n", "<leader>cR", "TypescriptRenameFile", { desc = "Rename File", buffer = buffer })

        end)

      end,

    },

    ---@class PluginLspOpts

    opts = {

      ---@type lspconfig.options

      servers = {

        -- tsserver will be automatically installed with mason and loaded with lspconfig

        tsserver = {},

      },

      -- you can do any additional lsp server setup here

      -- return true if you don't want this server to be setup with lspconfig

      ---@type table<string, fun(server:string, opts:_.lspconfig.options):boolean?>

      setup = {

        -- example to setup with typescript.nvim

        tsserver = function(_, opts)

          require("typescript").setup({ server = opts })

          return true

        end,

        -- Specify * to use this function as a fallback for any server

        -- ["*"] = function(server, opts) end,

      },

    },

  },


  -- for typescript, LazyVim also includes extra specs to properly setup lspconfig,

  -- treesitter, mason and typescript.nvim. So instead of the above, you can use:

  { import = "lazyvim.plugins.extras.lang.typescript" },


  -- add more treesitter parsers

  {

    "nvim-treesitter/nvim-treesitter",

    opts = {

      ensure_installed = {

        "bash",

        "html",

        "javascript",

        "json",

        "lua",

        "markdown",

        "markdown_inline",

        "python",

        "query",

        "regex",

        "tsx",

        "typescript",

        "vim",

        "yaml",

      },

    },

  },


  -- since `vim.tbl_deep_extend`, can only merge tables and not lists, the code above

  -- would overwrite `ensure_installed` with the new value.

  -- If you'd rather extend the default config, use the code below instead:

  {

    "nvim-treesitter/nvim-treesitter",

    opts = function(_, opts)

      -- add tsx and treesitter

      vim.list_extend(opts.ensure_installed, {

        "tsx",

        "typescript",

      })

    end,

  },


  -- the opts function can also be used to change the default opts:

  {

    "nvim-lualine/lualine.nvim",

    event = "VeryLazy",

    opts = function(_, opts)

      table.insert(opts.sections.lualine_x, {

        function()

          return "😄"

        end,

      })

    end,

  },


  -- or you can return new options to override all the defaults

  {

    "nvim-lualine/lualine.nvim",

    event = "VeryLazy",

    opts = function()

      return {

        --[[add your custom lualine config here]]

      }

    end,

  },


  -- use mini.starter instead of alpha

  { import = "lazyvim.plugins.extras.ui.mini-starter" },


  -- add jsonls and schemastore packages, and setup treesitter for json, json5 and jsonc

  { import = "lazyvim.plugins.extras.lang.json" },


  -- add any tools you want to have installed below

  {

    "williamboman/mason.nvim",

    opts = {

      ensure_installed = {

        "stylua",

        "shellcheck",

        "shfmt",

        "flake8",

      },

    },

  },

}

d. to disable a plugin, add the following

if true then

  return {}

end

20 : After update nvim packages 

        a. Example : Lazy with U or LazyExtras

        b. Example : update node version

        c. Example : update golang version

        What will happen : some package need update

              1. gopls : go install golang.org/x/tools/gopls@latest

              2. mason update : :MasonUpdate

              3. :MasonInstall gopls

21 : Code trace for search function : <leader>-/

I'll help you find how `<leader>-/` is defined in your Neovim configuration and which function it calls. Let me search through your configuration files. 


Searched codebase for "leader / keymap mapping toggle comment", 16 results


Let me search more specifically for the `<leader>/` keymap pattern: 


Searched for text `<leader>/`, 12 results


Great! Now let me look at the LazyVim core configuration files to understand which picker is being used by default: 


Read [](file:///Users/holmaschen/.local/share/nvim/lazy/LazyVim/lua/lazyvim/plugins/extras/editor/snacks_picker.lua)


Now let me check the LazyVim configuration to see which picker is being used and examine the `LazyVim.pick()` function: 


Searched for text `LazyVim.pick` (`**/lazy/LazyVim/lua/**`), 20 results


Let me examine the core LazyVim pick utility module: 


Searched for text `LazyVim.pick` (`**/lazy/LazyVim/lua/lazyvim/util/**`), 4 results


Read [](file:///Users/holmaschen/.local/share/nvim/lazy/LazyVim/lua/lazyvim/util/pick.lua)


Now let me check which picker is currently active in your LazyVim setup. Let me look for plugin configuration files that might define which picker is loaded: 


Searched for files matching `**/init.lua`, 10 matches


Let me look for your Neovim configuration outside the lazy plugin directory: 


Read [](file:///Users/holmaschen/.local/share/nvim)


Let me check your Neovim config directory (typically in `~/.config/nvim`): 


Searched for files matching `**/~/.config/nvim/**`, no matches


Ran terminal command: find ~ -name "nvim" -type d 2>/dev/null | head -5


Let me check your actual Neovim configuration: 


Read [](file:///Users/holmaschen/.config/nvim)


Let me check what picker is currently active by looking for any loaded configurations or running a Neovim command: 


Ran terminal command: nvim --headless -c "lua if LazyVim and LazyVim.pick and LazyVim.pick.picker then print(LazyVim.pick.picker.name) else print('LazyVim not loaded') end" +q 2>/dev/null || echo "Could not determine picker"


Let me check what is likely configured by looking at your loaded plugins: 


Searched codebase for "telescope installed configured keymap picker settings", 26 results


Based on my search through your Neovim configuration, I can now explain how `<leader>/` is defined and which function it calls.


## How `<leader>/` is defined


The `<leader>/` keymap is defined in different ways depending on which picker plugin is active in your LazyVim configuration. Looking at your setup, there are three possible picker plugins that could be active:


1. **Snacks Picker** (most likely)

2. **Telescope**

3. **FZF-lua**


## Current Implementation


Based on the search results, your configuration appears to be using **Snacks Picker** (the default for LazyVim). Here's how `<leader>/` is defined:


In snacks_picker.lua, line 60:


```lua

{ "<leader>/", LazyVim.pick("grep"), desc = "Grep (Root Dir)" },

```


## Function Call Chain


When you press `<leader>/`, the following function chain is executed:


1. **`LazyVim.pick("grep")`** - This is a wrapper function from pick.lua


2. **`LazyVim.pick.wrap("grep", opts)`** - Returns a function that calls:


3. **`LazyVim.pick.open("grep", opts)`** - Which translates the command and calls:


4. **`M.picker.open(command, opts)`** - Where `M.picker` is the registered picker (Snacks)


5. **`Snacks.picker.pick("grep", opts)`** - The final function that opens the grep interface


## Specific Function Called


The ultimate function that gets called is:


```lua

Snacks.picker.pick("grep", opts)

```


This opens the Snacks picker in grep mode, allowing you to search for text across all files in your project's root directory.


## Alternative Implementations


If you were using different pickers, the keymap would resolve to:


- **Telescope**: `require("telescope.builtin").live_grep(opts)`

- **FZF-lua**: `require("fzf-lua").live_grep(opts)`


The beauty of LazyVim's picker abstraction is that `<leader>/` always provides the same functionality (project-wide text search) regardless of which picker backend you're using, but the underlying implementation changes based on your configuration.



Let me examine how `Snacks.picker.pick("grep", opts)` actually works by looking into the Snacks.nvim source code. 


Read [](file:///Users/holmaschen/.local/share/nvim/lazy/snacks.nvim/lua/snacks/picker/init.lua)


Now let me look at the grep source implementation: 


Searched codebase for "grep source snacks picker implementation command rg ag ripgrep", 22 results


Now let me look at the actual grep source file to understand the command it uses: 


Read [](file:///Users/holmaschen/.local/share/nvim/lazy/snacks.nvim/lua/snacks/picker/source/grep.lua)


Now let me also check the proc source to understand how the command execution works: 


Read [](file:///Users/holmaschen/.local/share/nvim/lazy/snacks.nvim/lua/snacks/picker/source/proc.lua)


Perfect! Now I have a complete understanding of how `Snacks.picker.pick("grep", opts)` works. Let me provide you with the detailed explanation.


## How `Snacks.picker.pick("grep", opts)` Actually Works


### 1. Command Used: **Ripgrep (`rg`)**


When you call `Snacks.picker.pick("grep", opts)`, it **uses `rg` (ripgrep)** as the underlying command, NOT `ag`, `grep`, or other tools. This is hardcoded in the source file:


```lua

local function get_cmd(opts, filter)

  local cmd = "rg"  -- Hardcoded to use ripgrep

  -- ... rest of the function

end

```


### 2. Command Arguments


The default `rg` command that gets executed looks like this:


```bash

rg --color=never --no-heading --with-filename --line-number --column --smart-case --max-columns=500 --max-columns-preview -g !.git -- "search_pattern"

```


### 3. How It Works Internally


Here's the complete flow:


1. **`LazyVim.pick("grep")`** → calls **`Snacks.picker.pick("grep", opts)`**


2. **Snacks picker** loads the **grep source** from `/snacks/picker/source/grep.lua`


3. **`get_cmd()` function** builds the ripgrep command with these default arguments:

   ```lua

   local args = {

     "--color=never",       -- No colored output

     "--no-heading",        -- Don't group matches by file

     "--with-filename",     -- Include filename in output

     "--line-number",       -- Include line numbers

     "--column",           -- Include column numbers

     "--smart-case",       -- Smart case matching

     "--max-columns=500",  -- Limit column width

     "--max-columns-preview",

     "-g", "!.git",       -- Exclude .git directory

   }

   ```


4. **Additional options** are dynamically added based on your configuration:

   - `--hidden` or `--no-hidden` (show hidden files)

   - `--no-ignore` (show ignored files)

   - `-L` (follow symlinks)

   - `-t <filetype>` (filter by file type)

   - `--fixed-strings` (literal search instead of regex)

   - `-g <glob>` (glob patterns)


5. **Process execution**: The command is executed via proc.lua using Neovim's `uv.spawn()` (libuv process spawning)


6. **Real-time output processing**: Results are streamed live as ripgrep finds matches, parsed, and displayed in the picker interface


### 4. Output Processing


The ripgrep output is parsed in real-time:


```lua

-- Example ripgrep output: "file.lua:123:45:some matching text"

local file, line, col, text = item.text:match("^(.+):(%d+):(%d+):(.*)$")

```


Each result is transformed into a structured item with:

- `file`: The file path

- `pos`: Line and column position `[line, col]`

- `line`: The matching text content


### 5. Why Ripgrep?


Snacks picker uses `rg` because it's:

- **Extremely fast** - faster than `ag` and traditional `grep`

- **Feature-rich** - supports smart case, file type filtering, glob patterns

- **Modern** - handles Unicode, respects `.gitignore` by default

- **Reliable** - well-maintained and widely adopted


### 6. No Fallback


Unlike some other tools, Snacks picker **only** uses ripgrep for grep functionality. There's no fallback to `ag`, `grep`, or other search tools. If `rg` is not available, the grep function would fail.


So to directly answer your question: **`Snacks.picker.pick("grep", opts)` calls the `rg` (ripgrep) command**, not `ag`, `grep`, or any other search tool.

沒有留言: