Golang 開發環境 - 使用 neovim 0-5

neovim 從 vim 專案 fork 之後,新增了 async 功能,所有工作不會再被單一線程給擋住 (在過去 linter 檢查時,是無法寫程式碼的),大大提升了可用性和使用者體驗,朝現代編輯器躍進了一大步。neovim 並沒有因此停下腳步,在之後的版本陸續推出了

2021 的 0.5 版本發佈,除了古老的文字介面,neovim 可說是不折不扣的現代編輯器。

一個現代的編輯器應該有以下幾種基本功能:

本篇將會使用短小的 lua 設定檔達到基礎 go 語言的開發環境,並產出 init.lua 檔案

Color scheme

選一個適合自己眼睛的主題吧,推薦 gruvbox-material

use {
    'sainnhe/gruvbox-material',
    setup = function()
        vim.g.gruvbox_material_background = 'soft'
    end,
    config = function()
        vim.cmd[[colorscheme gruvbox-material]]
    end
}

File explorer - nerdtree

雖然在上篇我不推薦使用 nerdtree,並且端出了需要高度手動配置的 defx.nvim。經過仔細思考之後發現這是個人偏好,不應該拉讀者一起折騰,沒有什麼特殊需求的話,大部分的情況下 nerdtree 是很夠用的,使用者體驗也不錯。老牌的 nerdtree 使用上相對穩定,也會找到較多的討論。

安裝 nerdtree

use {
	'preservim/nerdtree',
    setup = function()
    	-- Read the following nerdtree section and add what you need
    end
}

映射 <F4> 打開或關閉 nerdtree

vim.api.nvim_set_keymap("n", "<F4>", "<cmd>NERDTreeToggle<cr>" ,{silent = true, noremap = true})

在 nerdtree 只要會使用兩個按鍵就能存活

vim-go

強大的 vim-go 提供了各式各樣 go 語言的開發功能,每一個使用 vim 開發 go 的人一定都用過。它包括了 auto completion, code navigation, background compilation, code format… 功能,我將會介紹 vim-go 如何使用這些功能

安裝 vim-go

use {
    'fatih/vim-go',
    run = ':GoUpdateBinaries',
    ft = 'go',
    setup = function()
    	-- Read the following section and add what you need
	end
}

Auto-complete

vim-go 提供的 auto completion 使用 omnifunc 實作 (:help omnifunc),omnifunc 使用 <c-x><c-o> 觸發並彈出候選補全選單,按起來很不順手,為了更好的體驗,我將設定成:

local t = function(str)
    return vim.api.nvim_replace_termcodes(str, true, true, true)
end

local check_back_space = function()
    local col = vim.fn.col('.') - 1
    if col == 0 or vim.fn.getline('.'):sub(col, col):match('%s') then
        return true
    else
        return false
    end
end

_G.tab_complete = function()
    if vim.fn.pumvisible() == 1 then
        return t "<C-n>"
    elseif check_back_space() then
        return t "<Tab>"
    else
        return t "<C-x><C-o>"
    end
end

_G.s_tab_complete = function()
    if vim.fn.pumvisible() == 1 then
        return t "<C-p>"
    else
        return t "<C-h>"
    end
end

_G.enter_key = function()
    if vim.fn.pumvisible() == 1 then
        return t "<C-y>"
    else
        return t "<CR>"
    end
end

vim.api.nvim_set_keymap("i", "<tab>", "<C-R>=v:lua.tab_complete()<CR>" ,{silent = true, noremap = true})
vim.api.nvim_set_keymap("i", "<s-tab>", "<C-R>=v:lua.s_tab_complete()<CR>" ,{silent = true, noremap = true})
vim.api.nvim_set_keymap('i', '<enter>', '<C-R>=v:lua.enter_key()<CR>' ,{silent = true, noremap = true})

Code navigation - vim-go

vim.api.nvim_set_keymap('n', 'gr', '<Plug>(go-referrers)')

Background compile - vim-go

vim-go 提供了編譯和執行的指令,可以輕鬆在 vim 裏編譯、執行

Code format - vim-go

Async linter - 使用 nvim-lspconfig

安裝 nvim-lspconfig 並且設定 gopls

use {
    'neovim/nvim-lspconfig',
     config = function()
		require('lspconfig').gopls.setup{}
  		-- Read the following lspconfig section and add what you need
     end
}

將 diagnostic 資訊顯示在程式碼旁

diagnostic_config = {
    -- Enable underline, use default values
    underline = true,
    -- Enable virtual text, override spacing to 2
    virtual_text = {
        spacing = 2,
        prefix = '<',
    },
    -- Use a function to dynamically turn signs off
    -- and on, using buffer local variables
    signs = function(bufnr, client_id)
        local ok, result = pcall(vim.api.nvim_buf_get_var, bufnr, 'show_signs')
        -- No buffer local variable set, so just enable by default
        if not ok then
            return true
        end

        return result
    end,
    -- Disable a feature
    update_in_insert = false,
}

vim.lsp.handlers["textDocument/publishDiagnostics"] = vim.lsp.with(
    vim.lsp.diagnostic.on_publish_diagnostics, diagnostic_config)```
        - 在 diagnostic 之間跳轉
            - 映射 `[d` `]d` 跳轉到上一個和下一個 diagnostic
            - ```plain text
vim.api.nvim_set_keymap('n', '[d', '<cmd>lua vim.lsp.diagnostic.goto_prev()<CR>',{silent = true, noremap = true})
vim.api.nvim_set_keymap('n', ']d', '<cmd>lua vim.lsp.diagnostic.goto_next()<CR>',{silent = true, noremap = true})

Code outline - tagbar

安裝 gotags:

$ go get -u github.com/jstemmer/gotags

直接照 gotags 文件的設定

use {
    'preservim/tagbar',
    cmd = 'TagbarToggle',
    setup = function()
    	-- Read the following tagbar section and add what you need
    end
}

直接照 gotags 文件的設定

vim.g.tagbar_type_go = {
    ctagstype = 'go',
    kinds = {
        'p:package',
        'i:imports:1',
        'c:constants',
        'v:variables',
        't:types',
        'n:interfaces',
        'w:fields',
        'e:embedded',
        'm:methods',
        'r:constructor',
        'f:functions'
    },
    sro = '.',
    kind2scope = {
        t = 'ctype',
        n = 'ntype',
    },
    scope2kind = {
        ctype = 't',
        ntype = 'n'
    },
    ctagsbin  = 'gotags',
    ctagsargs = '-sort -silent'
}

映射 開啟或是關閉 tagbar

vim.api.nvim_set_keymap("n", "<F8>", "<cmd>TagbarToggle<cr>" ,{silent = true, noremap = true})

Fuzzy finder - Telescope

作為 neovim built-in LSP client 的主要開發者 tjdevries 所主導開發的 fuzzy finder,早晚會成為 neovim 界 fuzzy finder 的主流,在大部分的使用情境下可以取代 fzf.vim,並且使用 lua 開發。

Telescope 的 live_grep 和 find_files 是預設使用 ripgrep 安裝 rigrep

安裝 telescope

use {
    'nvim-telescope/telescope.nvim',
    requires = {{'nvim-lua/popup.nvim'}, {'nvim-lua/plenary.nvim'}},
    cmd = 'Telescope',
    setup = function()
    -- Read the following telescope section and add what you need
    end
}

映射 <leader>fg 即時文字搜索

vim.api.nvim_set_keymap('n', '<leader>fg', '<cmd>lua require('telescope.builtin').live_grep()<cr>', {noremap = true})

映射 <leader>ff 尋找 file

vim.api.nvim_set_keymap('n', '<leader>ff', '<cmd>lua require('telescope.builtin').find_files()<cr>', {noremap = true})

最後完成的 init.lua,貼上內容重開 neovim 後,記得使用 PackerSync 安裝套件

本篇的用意和上篇一樣,盡量用精簡的設定檔和少量的套件打造一個短小精幹的 go 語言基礎開發環境,但使用效果和體驗和上篇效果相當,大部分的 go 語言開發功能都透用 vim-go 達成。

雖然我針對了了七個從 coloscheme 到 fuzzy finder 重點功能,但這其實只是個開始,你應該把這篇所寫的設定檔當成一個骨架繼續改進,甚至只要記這七個重點功能的用意,替換成你覺得更棒的,比如說你覺得 fzf 比 telescope 更棒,或是你覺得 ycm 補全比 vim-go 內建的更優秀,那就換吧,往 "打造成自己最合適的開發環境" 這條道路前進。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源:https://amikai.github.io/2021/08/16/go_neovim_env_0.5/