前言

Neovim 同時支援使用 VimScript 和 Lua 語言來撰寫設定文件,前者源自於它的老祖 Vim,而後者則是一種極爲輕量的 Scripting language,同時亦是 Neovim 有別於 Vim 最大的特點之一:它更加易學、具有更好的工具支持與易擴展性、以及更高的執行效能,這都是你應該選擇使用 Lua 而非 VimScript 原因,唯一導致你必須使用 VimScript 的理由大概只有,當你想與 Vim 共用你的這份設定文件的時候。

有鑑於我已經不太使用 Vim 了,且我也更加喜歡 Lua 的設定模式,故本系列文都將使用 Lua 來撰寫設定文件,如果你是從 Vim 轉過來的 VimScript 使用者,放心,Lua 比 VimScript 簡單多了,順着設定文件讀下去,想必就能學會個七、八成了吧。

在本章裡,我將介紹 Neovim 的設定文件結構,分享與說明我的基礎功能設定,以及安裝部分依賴程式以啟用特定功能,如果想直接看我的設定和設定項說明,也可以直接跳到 基礎設定說明 一段閱讀。

而如果想直接複製我的設定文件,可以到這裡(我的 github)來複製完整版的內容。


建立設定文件與引用

設定文件在哪裡?

遇事不決,先問 help page,讓我們輸入:h config,來看看有沒有什麼有用的資訊:

有了,在 Unix 環境下,Config file 的位置應該在 ~/.config/nvim/init.lua,而這也就是 Neovim 每次在起動時,讀取設定文件的入口處。

註:如果你在電腦中找不到文件,甚至相關的資料夾,請直接創建一個就可以了。

接著,讓我們來試寫第一段 Lua 程式,看看 Neovim 會不會正常運行它吧,編輯 init.lua

1
2
print("Hello!")
print("World!")

重新啓動 Neovim,你應該就可以看到文字訊息被打印出來了。

另外,如果你因爲輸入了什麼而錯過任何系統訊息,想重看完整內容的話,可以在開啟 Neovim 後,輸入:message來查看所有的歷史訊息,或是輸入:{count} mes 來查看最近的 {count} 筆訊息,例如:1 mes

建立文件結構

以下將是我們接下來會採用的文件結構:

  • init.lua:作為設定文件的入口,負責引用其他的設定文件。

  • lua/:當我們在 Lua 中引用其他文件時,Lua 會優先引用./lua/?.lua中的文件,如果不存在,才會去使用./?.lua中的文件。

    例如:當我們在init.lua中引用 test.lua時,Lua 會優先使用 ./lua/test.lua,如果不存在,才會去使用./test.lua

    我的建議是,為了避免將來管理混亂,還是乖乖地把所有設定檔放在lua/裡頭會比較好。

  • imp/:這層資料夾將作為命名空間,用意是避免當 Lua 在引用其他外掛的設定時,出現意外的文件名稱衝突,你可以將這個資料夾以任何你喜歡的方式命名,例如:user/launch/、或是和我一樣以 id 的前三碼命名。

  • options.lua:這將會是我們的第一個設定文件,用來對 Neovim 的基礎功能進行設定,詳細內容將在後續介紹。

引用設定文件

最後,讓我們來引用我們的第一個設定文件吧,編輯init.lua

1
require("imp/options")
  • require("{filename}"):此函式將會引用./lua/{filename}.lua

接著做為測試,讓我們編輯options.lua

1
print("Hi, I'm options")

重啓 Neovim,此時我們就可以看到option.lua的內容被成功引用了:


基礎設定說明

Lua API

Neovim 提供了一套完整的 Lua library,其所提供的 API 讓我們的設定文件可以輕鬆使用 Lua 語言來與 Neovim 進行交互。這些 API 大多都以vim.做爲前綴,例如:

API 用途 例子 對應的 VimScript
vim.opt 用於設置和訪問全局選項 vim.opt.number = true set number
vim.g 用於設置和訪問全局變量 vim.g.my_var = 'hello' let g:my_var = 'hello'
vim.cmd 用於執行 Vim 命令(Ex 命令) vim.cmd("new") :new

所以,我們可以如此編輯 options.lua

vim.opt.number = true

儲存後重新起動 Neovim,你就會發現左側的行號被開啓了。Neovim就是透過這樣的方式,在每次啟動時引用設定文件,並根據其中的設定來變更編輯器的行為。

設定選項的魔法 —— :options

如果每次測試設定選項都必須要一項一項的查找,並反覆開關 Neovim 來測試效果的話,那也太麻煩了,所幸,Neovim 提供了即時改變選項的魔法:options

這裡提供了所有設定的分類目錄,以及各選項的簡述與選項內容,我們同樣可以使用/進行關鍵字搜尋,或將遊標移動到有興趣的目錄上,並按下<Enter>來查看目錄內容。

接著,我們可以將遊標移動到設定的選項上,然後按下<Enter>,或甚至直接修改字串內容,就可以發現選項被套用了。

這個設定不會被記錄,所以你可以隨意嘗試各種不同的設定,再將你喜歡的一條條記錄到設定檔中,我相當推薦各位有空的時候可以來這裡摸索一下,有時或許會發現一些神奇又實用的功能。

options.lua 完整說明

以下我將對我的設定項目進行分類介紹,完整的設定檔可以到這裡(我的 github)查看,你可以直接複製再根據需求進行微調;而由於項目有點多,且有部分我已經標上註解了,所以我這裡只會對一些我認為比較重要的,以及比較容易有疑慮的選項進行說明。

basic

0
1
2
3
4
5
6
7
-- basic --
vim.opt.fileencoding = "utf-8"
vim.opt.termguicolors = true
vim.opt.clipboard = "unnamedplus" -- allows nvim to access the OS clipboard
vim.opt.mouse = "a" -- enabled mouse, "a" for all modes, and "niv" for normal, insert, visual mode
vim.opt.timeoutlen = 500 -- Time in milliseconds to wait for a mapped sequence to complete
vim.opt.virtualedit = "block"
vim.opt.iskeyword:append("-") -- consider string-string as whole word
  • clipboard
    剪貼簿應該是每個剛入坑 Neovim 的人都會遇到的問題之一,你會發現你在 Neovim 外複製的東西無法直接使用指令p來貼上,同樣地,我們在 Neovim 中使用y複製的東西,也無法貼到 Neovim 外的其他地方,這是因爲 Neovim 預設的剪貼簿和系統是不同的,所以我們需要對其進行設定。
    另外,我們還需要在系統中額外安裝剪貼簿工具,才能讓它正常作用,我這裡使用的是 Xclip:
    sudo apt-get install xclip
    安裝完成後,複製貼上的功能應該就可以正常使用了。
  • virtualedit
    在預設情況,當我們使用 V-Block 模式時,我們是無法將遊標移動到行尾之後的部分的,即便圈選的其他行在這之後仍有內容:

    而當我們將其設為block之後,Neovim 就可以將這樣的行尾視為空白一起圈選起來了。

    另外,它還提供了其他模式,不過我沒有使用過,所以這裡就不做贅述了。
  • iskeyword:append("-")
    當我們使用w等指令進行單字間的跳躍時,Neovim 會把一些被特殊符號(例如:’-‘)所隔開的字視為兩個獨立的單字,我們可以透過這條設定,來將被這些符號分割的文字視為一個完整的單字。

backup

10
11
12
-- backup --
vim.opt.swapfile = false
vim.opt.undofile = true -- enable persistent undo
  • swapfile
    當不正常關閉 Neovim 時,它會爲當下正在開啓的檔案建立備份,並在下一次開啓時詢問要不要復原備份,由於我有動不動就存檔的習慣,以及它有時不知道爲什麼會到處出現,讓我覺得很煩,所以我習慣將它關閉。
  • undofile
    如果你希望可以在關閉文件後,在下一次開啓時還可以保留上次的操作記錄,來讓你使用指令uC-r來還原上/下一步,那麼你可以選擇將這個選項開啓。

line number and line column

14
15
16
17
18
-- line number and line column --
vim.opt.number = true
vim.opt.relativenumber = true
vim.opt.numberwidth = 2
vim.opt.signcolumn = "yes"
  • relativenumber
    相對行號在進行跳轉和多行操作時很有用,我相當推薦將它開啓。而下圖則是官方提供的number(nu)relativenumber(rnu)開關時的顯示對照圖:

tab and indentation

20
21
22
23
24
25
26
27
-- tab and indentation --
vim.opt.tabstop = 4
vim.opt.softtabstop = 4
vim.opt.shiftwidth = 4
vim.opt.smarttab = false
vim.opt.expandtab = true -- expand tab to spaces
vim.opt.autoindent = true -- copy indent from current line when starting a new line
vim.opt.smartindent = true -- make above smarter
  • tabstop:指的是當文件中有 tab 字符時,會將其顯示成幾個空格的寬度,以及當在 insert mode 按下<Tab>時,會將連續多少的空格轉換成 tab 字符。
  • softtabstop:當在 insert mode 按下<Tab>時,會插入多少個空格。
  • shiftwidth:當使用>><<進行縮排,以及新增一行時自動縮排所移動的空格數。

    註:這可能有點複雜,舉個例子,當設定為tabstop=8softtabstop=4shiftwidth=2時,在 insert mode 中按第一次<Tab>會插入4個空格,再按一次則會將8個空格轉為1個 tab 字符,按第三次則會變成1個 tab 字符加上4個空格,以此類推。
    而當我使用>><<,或是新增一行符合自動縮排的條件時,則整行會移動2個空格。

  • smarttab:當開啓時,當在行首進入 insert mode 插入<Tab>時,會根據shiftwidth的設定來插入空格,否則會插入根據softtabstop來插入,我覺得這相當混淆人,所以我選擇把它關閉。
  • expandtab:將所有 tab 字符都擴展成空格,如果設為true,則上述的轉換規則就不適用了,所有的 tab 都會變成相應數量的空格。

wrap and scrolloff

29
30
31
32
-- wrap and scrolloff --
vim.opt.wrap = false -- line wrapping
vim.opt.scrolloff = 7 -- minimal number of screen lines to keep above and below the cursor
vim.opt.sidescrolloff = 10
  • wrap:當你的文字長度超出視窗邊框時,是否換行顯示。
  • scrolloff:當視窗 上/下 捲動時,顯示在遊標 上/下 的行數,如果你希望畫面捲動時,遊標始終在正中央的位置,可以選擇把它設成 999 之類的極大數字。
  • sidescrolloff:和 scrolloff 相同,不過是左右方向的。
34
35
36
37
38
-- search --
vim.opt.hlsearch = false
vim.opt.incsearch = true -- search while typing a search command
vim.opt.smartcase = true -- if you type a capital letter, it will be case sensitive
-- vim.opt.ignorecase = true
  • smartcase:當搜尋的 pattern 中有大寫時,搜尋會變成 case sensitive,否則 ignore case,另外如果將它設為 true,它會覆蓋掉 ignorecase 選項的設定。

splitting windows

40
41
42
43
-- splitting windows --
vim.opt.splitright = true -- split vertical widow to the right
vim.opt.splitbelow = true -- split horizontal widow to the below
vim.opt.equalalways = false -- don't resize windows when splitting

當分割視窗時,Neovim 預設分割新視窗是開在左上的,這太反人類了,所以你可以透過第一二條設定來把它改成開在右下。

equalalways 的作用則是當分割出新視窗時,要不要自動調整平分所有視窗的長寬,如果設為 false,則它只會將最右或最下側的視窗平分而已

  • equalalways = true

  • equalalways = false

appearance

45
46
47
48
49
50
-- appearance --
vim.opt.showtabline = 2 -- always show tabline
vim.opt.cursorline = true
vim.opt.cmdheight = 1
vim.opt.pumheight = 10 -- pop up menu height
vim.opt.showmode = false -- with lualine, we don't need to show things like -- insert -- anymore
  • showtabline:設為 0 時永不顯示,1 時只有當存在兩個 tab 以上時才會顯示,2 則永遠顯示。
  • cursorline:開啓會將遊標所在行的行數加上醒目提示,如果想將所在列也標亮,可以調整cursorcolumn選項。
  • pumheight:顯示彈出式菜單的最大行數。(之後使用 cmp 功能時才會有比較明顯的體現。)

auto-commenting

52
vim.cmd("autocmd bufenter * set formatoptions-=cro") -- disable auto-commenting new lines

如果你希望在註解行上用例如oO等指令新增空白行時,會自動加上註解標籤的話,可以把這行刪掉,而我不喜歡它,所以我這裡把它關掉了。


依賴環境安裝與設定

還記得我們之前提過的:checkhealth嗎,如果你現在打開它,應該會發現在 provider 一區裡,經過我們剛才對剪貼簿的處理,Clipboard 已經顯示為 OK 了:

接下來,由於我經常使用到 Python3 的開發環境,加上之後安裝的許多外掛也可能依賴於 Python3 provider,所以我這裡會將它安裝和設定起來。

安裝 python3

Ubuntu 22.04 中應該已經自帶 python3.10,如果沒有的話,可以透過以下的指令下載。

sudo apt-get install software-properties-common -y
sudo add-apt-repository ppa:deadsnakes/ppa
sudo apt-get install python3.10 -y

安裝 pip

pip 是 Python 的軟體包管理工具,是使用 Python 必須安裝的工具之一。

sudo apt-get install python3-pip -y

安裝 pynvim

pynvim 就是 Neovim 之所以依賴於 Python3 的原因了,他是 Neovim 在 python 中的外掛。

python3 -m pip install --user --upgrade pynvim

通常到這裏就結束了,不過如果你的系統中存在多個版本的 Python 的話,建議到 options.lua 中加入類似下面這條設定,來指定 Neovim 取用的 Python 路徑:

54
vim.g.python3_host_prog = "/bin/pythin3"

至此,再看:checkhealth provider的話,應該就可以看到 Python3 一區也都變 OK 了,而至於其他 provider,由於目前還沒用到,加上我其實也對它們不是很了解,所以我沒有多做處理了。

後記

至此,就是本章的所有內容了,Neovim 終於至少看起來像個人,但離稱手還是有相當大的差距,而在下一章裡,我將會介紹如何設定快捷鍵(Keymaps)。

與這章的後半段類似,我會分享並說明我的 keymaps 設定,裡頭有許多我覺得實用甚至堪稱魔法的神奇設定,將在下一章誠心推薦給各位。

上一篇: 救救我啊我救我 —— help 與 checkhealth
下一篇: Neovim 快捷鍵設定

參考資料