文章目錄
- 前言(勸退)
- neovim 安裝
- neovim 配置
- 配置文件位置
- 第一個 hello world 代碼
- 拆分 neovim 配置
- 正式配置 neovim
- 基礎配置
- 自定義鍵位
- Lazy 插件管理器配置
- tokyonight 插件配置
- BufferLine 插件配置
- 自動補全括號 / 引號 插件配置
前言(勸退)
neovim 是一個現代化,高擴展的 vim 編輯器 fork 版本,適合程序員打造極致高效的開發環境。
在正式開始 neovim 配置之前,我還是要勸退一下的。
很多人說使用 neovim 的都是變成高手,但我認為,能搞明白 neovim 一定是高手居多,但不一定都是頂尖的大牛,但起碼技術一定不差,也正因如此,很多人使用使用 neovim 單純是因為腦子一熱,導致最后不僅耽誤了手頭的工作還啥都沒學會。要學習neovim,建議先把 vim 的操作搞熟悉(可以全程使用 vim 的語法做出一個完整的項目,注意我說的只是語法),而 vim 學習的曲線前期有點難度,導致很多人也掛在了第一關
,所以:
如果你編程能力不足
,那么請不要碰 vim / neovim 等任何需要大量自定義的編輯器。IDEA / VsCode 就夠了。如果你對 "折騰" 這件事沒有太大興趣
,那么請不要碰 vim / neovim 等任何需要大量自定義的編輯器。IDEA / VsCode 就夠了。雖然也有現成的 LazyVim,但是我不建議這樣做(原因后面會講)。
neovim 安裝
1)github 上搜索 neovim,在 Releases 中找到一個最近發的穩定版(https://github.com/neovim/neovim/releases),下載
安裝一路 next 即可。
2)打開終端,輸入 nvim test.txt 可以正常打開文本編輯器即表示安裝成功
neovim 配置
配置文件位置
neovim 會在啟動時加載 init.lua 文件,當我們修改了配置文件之后,重啟 neovim 即可生效(雖然也可以通過 :so
實現,但是配置復雜了之后,就不好用了)
1)init.lua 文件的位置在哪?(以下路徑和lua文件如果不存在,需要手動創建)
- Windows:
~/AppData/Local/nvim/init.lua(~ 表示 C:\Users\${你的用戶名}\)
- Unix:
~/.config/nvim/init.lua
2)你也打開終端,輸入 nvim 打開 neovim 編輯器,然后輸入 :=vim.fn.stdpath("config")
來查看配置文件位置.
:
表示 vim 中的命令模式=
表示執行 lua 代碼并輸出結果(當然,你也可以通過:lua
執行代碼,但是不會有任何輸出結果)vim.fn.stdpath("config")
表示答應配置文件的完整路徑
輸出結果如下:
第一個 hello world 代碼
1)通過 nvim init.lua
編輯配置文件,如下:
2)保存退出后,輸入 nvim
重新打開 neovim,可以看到左下角的顯示,如下圖,表示配置生效。
拆分 neovim 配置
隨著我們的配置不斷增多,全寫到一個 init.lua 文件中肯定是不利于維護的,因此我們將配置拆分成多個模塊。
一個通用的做法如下:
1)在 init.lua 的同級目錄下創建 lua 文件夾,然后再 lua 文件夾下創建 module.lua 文件,文件內容輸入print("hello world")
(為了驗證后續正確引入)。
2)那么就可以在 init.lua 中使用 require("module")
引入 module.lua 文件。
在 neovim 中,打開文件的命令是:e <filename>
,因此在通過 nvim 創建 module.lua 之后,直接可以通過 e: ./init.lua
打開 init.lua 文件
Ps:e 表示 edit,編輯的意思。
在 init.lua 中使用 `require(“module”),如下:
3)保存退出后,再打開 nvim,可以看到左下角 hello world
打印正常,表示上述操作步驟正確。
Ps:這里只是簡單認識一下怎么拆分模塊,后續的操作步驟中,會把上面演示的 module.lua 刪掉(包括 init.lua 中 require 也刪掉),開始正式配置 neovim。
正式配置 neovim
基礎配置
1)這里我習慣將基礎配置放到 ~/AppData/Local/nvim/lua/core/basic.lua
中。
同樣,記得在 init.lua 中引入這個文件,如下:
2)nvim 打開 basic.lua,配置如下:
-- 顯示行號
vim.opt.number = true
-- 相對行號
vim.opt.relativenumber = true-- 高亮當前光標所在行
vim.opt.cursorline = true-- 將 tab 水平制表符轉化成空格
vim.opt.expandtab = true
-- tab 長度設置成4個空格
vim.opt.tabstop = 4
-- shiftwidth 設置成0,可以避免換行后回車帶來的 tab 長度不一致問題
vim.opt.shiftwidth = 0-- 當 neovim 中打開的文件被其他程序修改了(例如idea),neovim 會自動加載
vim.opt.autoread = true
Ps:win 終端通過 cat 讀取 lua 文件會中文亂碼,可以通過
Get-Content .\lua\core\basic.lua -Encoding utf8
來查看.
自定義鍵位
1)這里先說一下,neovim 向下是兼容通過 vimscript 配置自定義鍵位的,但是我們既然使用 neovim,還是建議使用 lua 進行統一化配置。
2)自定義鍵位的 api:vim.keymap.set(mode, lhs, rhs, opts)
- mode:快捷鍵生效的模式.
- 不同模式用一個字母表示,例如 n 表示普通模式、i 表示插入模式、c 表示命令模式…
- 可以是 字符(單一模式生效,例如
n
表示在普通模式下生效),或者是 table(多模式生效,例如{'n', 'i'}
表示在普通模式和插入模式下都生效)
- lhs:表示要改哪個鍵位.
- 如果是普通鍵位,就直接寫了.
- 但如果要改 Ctrl + a,就要寫成
<C-a>
- 但如果要改 Alt + a,就要寫成
<A-a>
- rhs:表示要改成什么樣子,可以映射另一組按鍵,或者一個 lua 函數
- opts:是一個 table,包含了對快捷鍵一些額外的配置
3)案例一
vim.keymap.set("n", "<C-a>", ":lua print('hello world')<CR>", { silent = true })
- 在 normal 模式下生效.
- 按下
Ctrl + a
后,會打印 hello world,其中<CR>
表示回車 - silent = true 表示讓快捷鍵 “靜默執行”,不顯示執行過程中的命令或提示信息
演示如下:
然后按下 Ctrl + a
,效果如下:
4)案例二:如果想讓快捷鍵在 插入模式 下也生效.
錯誤的寫法如下(有坑):
vim.keymap.set({"n", "i"}, "<C-a>", ":lua print('hello world')<CR>", { silent = true })
真的可以這樣么?思考一下,因為我們在插入模式下,直接按下 <C-a>
,他會替換成 ":lua print('hello world')<CR>"
,相當于直接把這段話直接寫下來了(模擬鍵盤輸入:lua print('hello world')<CR>
的場景),如下:
正確的寫法如下:
vim.keymap.set({"n", "i"}, "<C-a>", "<Cmd>lua print('hello world')<CR>", { silent = true })
<Cmd>
并不是一個具體的按鍵,只表示進入命令模式。
此時你在 insert模式 按下 Ctrl + a
雖然是正確的,但是看不到效果,因為在默認 neovim 會在右下加固定顯示你當前的模式,不過你也可以通過 :lua vim.opt.showmode = false
關閉他,然后在 insert 模式下按下 Ctrl + a
就可以看到效果了
5)rhs 也可以是一個函數,例如如下
vim.keymap.set("n", "<C-a>", function ()print("hello world")
end, { silent = true})
這里寫的是 lua 中的匿名函數,使用 end 表示結尾
6 )leader key
可以理解成在配置中定義了一個叫做 <leader>
的變量,實現復用效果。
例如如下,設置 leader key 為 空格,當我在 normal 模式下按下 空格 + a + a
,就會打印 cyk 字符串。
vim.g.mapleader = " "
vim.keymap.set("n", "<leader>aa", ":lua print('cyk')<CR>", { silent = true})
7)正式配置,創建 ~/AppData/Local/nvim/lua/core/keymap.lua
文件,如下(這里是我自己習慣的):
vim.keymap.set("i", "jk", "<esc>", { silent = true })vim.keymap.set("n", "L", "$", {silent = true})
vim.keymap.set("v", "L", "$", {silent = true})vim.keymap.set("n", "H", "^", {silent = true})
vim.keymap.set("v", "H", "^", {silent = true})
最后在 init.lua 中引入,如下:
require("core.keymap")
Lazy 插件管理器配置
1)neovim 的插件一般都需要從 github 上手動 clone 并在 neovim 中引用。但是隨著插件的逐漸增多,這種方式會變得非常繁瑣,這就需要使用插件管理器來自動化這個過程。我使用 lazy 插件管理器進行配置。
lazy.nvim 是 Neovim 的一款現代插件管理器,主打的是"懶加載"理念:只有當我使用某個插件時才加載他,從而極大的提升啟動性能。
lazy.nvim 會讀取 ~/AppData/Local/nvim/plugins/
目錄下的所有 lua 插件配置文件。常見的寫法如下:
return {"插件名", -- 如 "nvim-treesitter/nvim-treesitter"name = "別名", -- 可選,為插件指定別名event = "VeryLazy", -- 懶加載觸發時機,支持多種事件cmd = "命令名", -- 使用命令時加載ft = "lua", -- 打開某種文件類型時加載keys = {...}, -- 使用某些快捷鍵時加載opts = {...}, -- 插件的配置(直接傳入插件的 `setup` 或 `opts`)config = function() ... end, -- 更復雜的配置dependencies = {...}, -- 插件依賴
}
每項具體如何編寫,會在后續的使用中講到。
2)首先需要安裝 git 和 nerd font。git 就不多說了,這里主要講一下 nerd font(https://www.nerdfonts.com/font-downloads)
Ps:如果不安裝字體,將來很多符號無法顯示。
這里建議使用 JetBrainsMono Nerd Font
下載后解壓到你常用的目錄即可。
3)在 neovim 配置中自動安裝和加載插件管理器邏輯。
創建 ~/AppData/Local/nvim/lua/core/lazy.lua
文件,內容如下:
-- 構造 lazy.nvim 的安裝路徑
-- vim.fn.stdpath("data") 表示返回 neovim 的數據目錄,例如 win 是 ~/AppData/Local/nvim-data;linux/mac 是 ~/.local/share/nvim
-- .. 是 lua 用來拼接字符串的,
-- win 最后拼接成 ~/AppData/Local/nvim-data/lazy/lazy.nvim
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"-- 如果 lazypath 不存在(lazy.nvim 未安裝),就執行內部邏輯
if not vim.uv.fs_stat(lazypath) then-- 把 lazy.nvim 下載到 lazypath 目錄vim.fn.system({"git","clone","--filter=blob:none","https://github.com/folke/lazy.nvim.git","--branch=stable",lazypath})
end-- 把 lazy.nvim 加入到 neovim 的運行時路徑(runtimepath)的最前面
-- 這樣才能使用 require("lazy") 正確加載插件管理器
-- 此處的 : 表示 lua 中的方法調用(表示傳入一個方法)
vim.opt.rtp:prepend(lazypath)-- 加載 lazy.nvim 插件管理器,并調用 setup 函數
-- 傳入 {} 空表,表示還沒有配置任何插件,后續可以加
require("lazy").setup({})
保存后,記得在 init.lua 中引入,如下:
require("core.lazy")
保存退出后重新打開 neovim,你會發現卡住了,這是因為正在執行剛剛寫的命令(neovim 是單線程的,會阻塞主線程)。
4)打開 neovim,然后輸入 “:Lazy”,就可以看到一個面板如下:
例如按下 Shift + p
就可以看到插件的一些啟動時的性能情況
面板上其他選項的介紹:
- Install:用來安裝插件的,相當于運行
:Lazy install
,只安裝在 lazy.nvim 中聲明的,并且還沒有下載的插件。不會更新已有的插件 - Update:更新插件,相當于運行
:Lazy Update
,他會執行 git pull 來拉取所有插件的最新版本 - Sync:同步插件狀態(安裝缺失的插件 + 更新已安裝的插件 + 清理不在配置中的插件 + 構建插件),相當于執行
:Lazy sync
,通常用來保證插件和配置完全一致 - Clean:清理插件,相當于執行
:Lazy clean
,用來刪除配置中不再引用的插件 - Check:檢查插件狀態,相當于執行
:Lazy check
,用來刪除配置中不再引用的插件 - Log:查看插件管理器中日志的輸出,主要調試用的
- Restore:恢復快照,從 snapshot 文件中恢復插件狀態,回滾版本(需要提前使用
Lazy snapshot
創建快照) - Profile:性能分析,分析各個插件加載時的性能情況
- Debug:調試 lazy.nvim 插件管理器的自身行為,會打開一個窗口輸出插件加載時的詳細信息(哪些插件怎么加載的、懶加載生效情況、報錯信息、路徑、加載順序等),適合在插件不工作,配置失效、性能分析時使用
tokyonight 插件配置
1)如下配置,會讓 neovim 自動讀取 lua/plugins 目錄下的文件,用來對插件進行配置。
require("lazy").setup({spec = {{ import = "plugins" }}
})
然后創建 ~/AppData/Local/nvim/lua/plugins
文件夾,之后在這個此目錄下,每配置一個插件就是一個 lua 文件(這種寫法看起來直觀)。
此時使用 nvim 可能會報錯,不過不用管,這是因為 plugins 目錄下還沒有任何插件。
2)安裝 tokyonight 主題
創建 ~/AppData/Local/nvim/lua/plugins/tokyonight.lua
文件,內容如下(從 tokyonight 的 github 上就可以看到怎么配置):
return {-- 這里寫主題的 github 鏈接-- 但是由于插件基本都來自github,因此 lazy 會自動補全前綴 "https://github.com/",因此插件的前綴就不用寫了"folke/tokyonight.nvim"
}
保存退出后重新打開,就可以看到 lazy 就直接開始幫我們安裝了,然后輸入 :colorscheme tokyonight
,回車后就可以看到效果了
3)如果我們再重新打開 neovim 的時候就會發現,還需要繼續手動讓主題生效,為了解決這個問題,可以重寫 tokyonight 的 config 項,如下:
return {-- 這里寫主題的 github 鏈接-- 但是由于插件基本都來自github,因此 lazy 會自動補全前綴 "https://github.com/",因此插件的前綴就不用寫了"folke/tokyonight.nvim",-- 可以通過此配置項更換成 tokyonight 的淺色主題opts = {style = "day"},-- 下面的 config 本來是 lazy 默認配置好的,但是為了能在插件啟動時自定義一些東西(比如啟動時就加載好主題),可以重寫此配置-- 這個方法的第一個參數幾乎用不上,opts 就是上一個配置項config = function (_, opts)require("tokyonight").setup(opts) -- 這一步是 lazy 給我們自動配置好的,這里再寫一遍主要是因為我們自己手動重寫了此函數vim.cmd("colorscheme tokyonight") -- 執行這個命令end
}
Ps:大多數情況,是不用手動編寫 config 的,除非在 lazy 默認加載之外,還有些別的操作。
4)這里你可能會有一個疑問,為什么一定要把加載主題的配置寫到 config 中,而不寫外部去加載?
因為切換主題這個命令,必須要在插件加載完成之后才能執行。
例如如下,我這樣寫,就是會報 “找不到主題的錯誤”。
原因:lazy.nvim 是異步懶加載插件的(讀取所有的插件完之后,再統一加載),而 vim.cmd("colorscheme tokyonight")
這行代碼在 tokyonight 插件真正被加載之前,就被執行了,所以就會報錯 E185: Cannot find color scheme 'tokyonight'
BufferLine 插件配置
默認情況下,所有的 buffer 都是隱藏的,通過使用 bufferline 插件,可以使所有的 buffer 以類似瀏覽器標簽頁的方式呈現在頁面的最上方。
1)首先創建 ~/AppData/Local/nvim/lua/plugins/bufferline.lua
文件,內容如下:
return {"akinsho/bufferline.nvim",-- 此插件必須手動指定一下 opts 才會進行 setupopts = {}
}
2)使用 neovim 隨便打開一個文件,例如 nvim aaa
打開文件 aaa,就可以看到效果如下:
再使用 e bbb
編輯一個新文件 bbb,都是可以看到 tab 的
3)可以通過 :BufferLineCyclePrev
前往上一個 tab,使用 :BufferLineCyclePrev
前往下一個 tab… 但是這樣太麻煩了
但是我們使用的使 neovim 啊,用的就是的自定義,當然這里還有一種更強的自定方式,就是直接在插件上配置 keys 來自定義鍵位,例如如下:(我這樣配置主要是為了減少和 IDEA 相關操作遷移的成本)
return {"akinsho/bufferline.nvim",-- 此插件必須手動指定一下 opts 才會進行 setupopts = {},keys = {{ "<leader>bn", ":BufferLineCycleNext<CR>", silent = true },{ "<leader>bo", ":BufferLineCloseOthers<CR>", silent = true },{ "<leader>bp", ":BufferLineCyclePrev<CR>", silent = true },{ "<leader>bd", ":bdelete<CR>", silent = true },{ "<leader>bh", ":BufferLinePick<CR>", silent = true },},
}
Ps:keys 中配置的鍵位默認就是在 normal 模式中生效,不用手動指定 mode
4)此處重新打開 nvim 會發現 tab 不見了?這里涉及到一個高級特性就是 懶加載,實際上我們配置了 keys 他的實際含義是開啟懶加載,只有在按下自定義 keys 中的鍵位時開始加載
。
為什么要會這樣設計呢?因為 neovim 在啟動時會加載所有插件,一旦插件變多了,neovim 的打開速度會越來越慢,因此就設計成在 neovim 啟動后進入主頁用不上的插件就先不加載,只有用到這個插件的時候再加載。而 keys 就是啟動懶加載開關的其中之一(還有的后續會講到)
當然對于 bufferline 顯示 tab 這種在啟動后就會用到的,我們還是希望他不要懶加載,因此可以手動設置 lazy 屬性為 false,如下:
return {"akinsho/bufferline.nvim",-- 此插件必須手動指定一下 opts 才會進行 setupopts = {},keys = {{ "<leader>bn", ":BufferLineCycleNext<CR>", silent = true },{ "<leader>bo", ":BufferLineCloseOthers<CR>", silent = true },{ "<leader>bp", ":BufferLineCyclePrev<CR>", silent = true },{ "<leader>bd", ":bdelete<CR>", silent = true },{ "<leader>bh", ":BufferLinePick<CR>", silent = true },},lazy = false
}
5)目前的 bufferline 配置出來還是有點丑的,因為 icon 圖標,因此這里還需要一個插件給 tab 顯示小圖標。
但是由于這個插件只是輔助于 bufferline 的,因此沒有必要單獨在 plugins 下創建一個 lua 文件,可以通過 lazy 提供的 dependencies 屬性實現,如下:
return {"akinsho/bufferline.nvim",dependencies = {"nvim-tree/nvim-web-devicons"},-- 此插件必須手動指定一下 opts 才會進行 setupopts = {},keys = {{ "<leader>bn", ":BufferLineCycleNext<CR>", silent = true },{ "<leader>bo", ":BufferLineCloseOthers<CR>", silent = true },{ "<leader>bp", ":BufferLineCyclePrev<CR>", silent = true },{ "<leader>bd", ":bdelete<CR>", silent = true },{ "<leader>bh", ":BufferLinePick<CR>", silent = true },},lazy = false
}
保存后重新進入,效果如下:
自動補全括號 / 引號 插件配置
這個見名思意,輸入 {
,自動補全 }
,其他的也同理。
如果刪除 {
也會自動把配對的 }
刪除,其他也同理。
這時候我們可以思考一個關于優化的問題,這個插件在 neovim 啟動的時候真的需要加載么么?并不需要,因為他不涉及 ui 的渲染,并且 normal 模式下也不會用到,因此我們希望他進入 insert 模式下才需要使用到,此時就可以使用 event 屬性進行懶加載
,如下:
return {"windwp/nvim-autopairs",event = "InsertEnter", -- 僅在 insert 模式才加載此插件opts = {}
}
1)那么重新進入 neovim,并輸入 :Lazy
可以看到 nvim-autopairs 并沒有被加載。
2)當進入 insert 的模式之后,再查看就加載了,說明懶加載配置成功。
Ps:有的插件可以懶加載,但是是在 normal 模式下就使用的,然而已進入 neovim 就是 normal 模式,類似這種你不清楚什么時候進行懶加載的情況,就可以配置成
event = VeryLazy
,這也是 lazy 插件管理器提供的一種方式。