Golang IDE工具搭建(neovim篇)

种草很久的neovim工具使用

由于一直羡慕大神们用vim工具快速的编辑代码,并且羡慕能够不断优化适合自己的IDE工具。所以选择了neovim作为自己的IDE工具进行配置。 其实,自己是在vscode中使用的vim插件进行编辑,但是还是不能完全摆脱鼠标的操作,且一些功能在windows和macos上使用的不太一样,所以最终狠心学习一下neovim,以及开始尝试搭建自己的vim IDE工具。

目标

适合自己的才是最好的,之前看了很多大神使用的各种插件,确实效率很高,让人羡慕不已,但是单单是把他们的插件配置拿过来是没有用的,倒不如自己从头搭建一个属于自己的IDE。 插件的查找方式从自己的需求出发,列出自己可能会用到的一些功能,在搜索需要的插件,个人认为这种方式可能更适合自己。

插件管理工具

要想安装插件,必须有个趁手的管理工具,从网上搜索好多人建议使用vim-plug这个插件。

vim-plug插件安装过程

  • vim-plug插件github地址及安装方法:https://github.com/junegunn/vim-plug,安装方式参考github上的Readme说明即可。
  • raw.githubusercontent.com 访问不了的问题解决
    • 打开https://site.ip138.com/raw.Githubusercontent.com/,输入raw.githubusercontent.com,查询解析IP地址,选取一个IP地址,更改/etc/hosts,直接将raw.githubusercontent.com域名指向该IP地址。
    • 重新执行github上的安装命令即可。

vim插件搜索网站

根据需求搜索需要的plug

vim-plug插件管理

  • neovim配置文件所在位置./config/nvim/init.vim
  • 向init.vim中写入自己的插件内容
  • for example
" Specify a directory for plugins
" - For Neovim: stdpath('data') . '/plugged'
" - Avoid using standard Vim directory names like 'plugin'
call plug#begin('~/.vim/plugged') # 插件存放位置

" Plugin outside ~/.vim/plugged with post-update hook
Plug 'junegunn/fzf', { 'dir': '~/.fzf', 'do': './install --all' } # 插件在github上的名称

" Initialize plugin system
call plug#end()
  • 执行命令:PlugInstall命令安装插件

IDE一样的目录树结构

  • The NERD tree
  • Plug ‘scrooloose/nerdtree’
  • Plug ‘preservim/nerdtree’ 都是可以的,对应的github路径都是:https://github.com/preservim/nerdtree
  • init.vim文件内容
call plug#begin('~/.vim/plugged')
" https://vimawesome.com/plugin/nerdtree-red
" github: https://github.com/preservim/nerdtree
" Plug ‘scrooloose/nerdtree’ is the same
Plug 'scrooloose/nerdtree'
autocmd vimenter
call plug#end()
切换工作台和目录
ctrl + w + h    光标 focus 左侧树形目录
ctrl + w + l    光标 focus 右侧文件显示窗口
ctrl + w + w    光标自动在左右侧窗口切换
ctrl + w + r    移动当前窗口的布局位置
基本操作命令
o       在已有窗口中打开文件、目录或书签,并跳到该窗口
go      在已有窗口 中打开文件、目录或书签,但不跳到该窗口
t       在新 Tab 中打开选中文件/书签,并跳到新 Tab
T       在新 Tab 中打开选中文件/书签,但不跳到新 Tab
i       split 一个新窗口打开选中文件,并跳到该窗口
gi      split 一个新窗口打开选中文件,但不跳到该窗口
s       vsplit 一个新窗口打开选中文件,并跳到该窗口
gs      vsplit 一个新 窗口打开选中文件,但不跳到该窗口
!       执行当前文件
O       递归打开选中 结点下的所有目录
x       合拢选中结点的父目录
X       递归 合拢选中结点下的所有目录
e       Edit the current dif

D       删除当前书签

P       跳到根结点
p       跳到父结点
K       跳到当前目录下同级的第一个结点
J       跳到当前目录下同级的最后一个结点
k       跳到当前目录下同级的前一个结点
j       跳到当前目录下同级的后一个结点

C       将选中目录或选中文件的父目录设为根结点
u       将当前根结点的父目录设为根目录,并变成合拢原根结点
U       将当前根结点的父目录设为根目录,但保持展开原根结点
r       递归刷新选中目录
R       递归刷新根结点
m       显示文件系统菜单
cd      将 CWD 设为选中目录

I       切换是否显示隐藏文件
f       切换是否使用文件过滤器
F       切换是否显示文件
B       切换是否显示书签

q       关闭 NerdTree 窗口
?       切换是否显示 Quick Help

切换标签页
:tabnew [++opt选项] [+cmd] 文件      建立对指定文件新的tab
:tabc   关闭当前的 tab
:tabo   关闭所有其他的 tab
:tabs   查看所有打开的 tab
:tabp   前一个 tab
:tabn   后一个 tab

normal模式下:
gT      前一个 tab
gt      后一个 tab

##### 创建删除文件/文件夹
ma: 创建文件或者文件夹,文件名称以/结尾就是文件夹了
md: 删除光标所在的文件

自动代码补全

coc.vim

配置

安装扩展插件,如下:

:CocInstall coc-json coc-tsserver

coc-json

配置go语言LSP,如下:

:CocConfig

{
  "languageserver": {
    "go": {
      "command": "gopls",
      "rootPatterns": ["go.mod"],
      "trace.server": "verbose",
      "filetypes": ["go"]
    }
  }
}
python语言配置
:CocInstall coc-python

coc-python

golang语言配置
  1. 方案一使用go-vim插件,添加如下配置到init.vim中
let g:go_def_mode='gopls'
let g:go_info_mode='gopls'
  1. 方案二,使用coc.vim中配置lsp到coc-setting.json文件中
{
  "languageserver": {
    "golang": {
      "command": "gopls",
      "rootPatterns": ["go.mod", ".vim/", ".git/", ".hg/"],
      "filetypes": ["go"],
      "initializationOptions": {
        "usePlaceholders": true
      }
    }
  }
}
  • 添加自动导入import 包,当文件中缺失要导入的包的时候,在init.vim中添加如下信息
autocmd BufWritePre *.go :call CocAction('runCommand', 'editor.action.organizeImport')

配置文件位置如下: cocConfig

快速代码跳转

easymotion

简单更改符号

vim-surround

golang开发环境

vim-go

let g:go_def_mode='gopls'
let g:go_info_mode='gopls'

参考:https://github.com/fatih/vim-go/wiki

![入门指导]https://github.com/fatih/vim-go/wiki/Tutorial

" autowrite, 执行:make时自动保存
set autowrite
" shortcut for find error
map <C-n> :cnext<CR>
map <C-m> :cprevious<CR>
nnoremap <leader>a :cclose<CR>
" shortcut for go run, go build
autocmd FileType go nmap <leader>b  <Plug>(go-build)
autocmd FileType go nmap <leader>r  <Plug>(go-run)

map: 递归映射 : 表示回车 : 表示Ctrl+n noremap: 表示非递归调用 nmap

let g:go_list_type = "quickfix"

在当前文件的基础上增加一个对此文件的test文件,可以直接使用edit xxx_test.go的方式,会自动添加此包的package信息

:edit main_test.go

Testing 执行go的Test测试

:GoTest #执行go test命令
:GoTestFunc #执行光标所在Test函数的测试

# init.vim 中可以设置go test超时时间,由于go test并不是异步的,所以需要有超时时间,万一test挂了呢
let g:go_test_timeout = '10s'

进一步简化go build与go test,根据文件的结尾内容和.go的后缀来判断该执行go test or go build

" run :GoBuild or :GoTestCompile based on the go file
function! s:build_go_files()
  let l:file = expand('%')
  if l:file =~# '^\f\+_test\.go$'
    call go#test#Test(0, 1)
  elseif l:file =~# '^\f\+\.go$'
    call go#cmd#Build(0)
  endif
endfunction

autocmd FileType go nmap <leader>b :<C-u>call <SID>build_go_files()<CR>

格式化go文件, 当保存的时候会自动格式化

:GoFmt
# 关闭保存文件时自动格式化
let g:go_fmt_autosave = 0

导入包

:GoImport strings # 导入strings包
:GoImport s # 通过tab键联想出要导入的包
:GoImportAs # 导入包的同时重定义名字
:GoDrop # 卸载包

自动导入缺失的包移除不需要的包,就是工具goimports

:GoImports #多了一个s
# 也可增加配置,在save的时候自动执行goimports
let g:go_fmt_command = "goimports"

if 和 af 快捷方式 if: 当光标所在函数范围时,执行dif/vif/yif,分别表示删除、选中、复制函数内容,但不包括函数标题和函数注释 af: 与if相似但是会包括函数的标题和注释,将函数注释作为函数的doc信息 如果不想将函数的注释作为doc信息可以执行

let g:go_textobj_include_function_doc = 0

gS和gJ:分别用来将结构体表达式拆分成多行和合并成一行,需要安装如下插件

Plug 'AndrewRadev/splitjoin.vim'

Snippets:快速添加代码片段,推荐安装插件 Ultisnips and neosnippet.此处我使用的是 Ultisnips

Plug 'SirVer/ultisnips'

snippets 的tab键与YouComplete的tab键冲突

golang相关的snippets可以参见如下路径

https://github.com/fatih/vim-go/blob/master/gosnippets/UltiSnips/go.snippets

举例:

errp: 
if err != nil {
    panic( )
          ^
          cursor position
}
type Foo struct {
	Name string # 此处输入json,则会自动添加`json:"name"`
}

Q: 发现一个问题就是在输入errp后没有提示信息,即没有一个列表显示可选项。但是直接输入tab确实能够生成代码,非常考验记忆力 A: 将go.snippets移动到~/.config/nvim/UltiSnips/go.snippets 中,好处可以通过维护自己的go.snippets

  • 快捷键:tab-j 跳向下一个可修改参数 tab-k 跳向上一个可修改参数

默认情况下json字符串的格式是下划线的形式,如果想要使用驼峰式,则可以进行如下修改:

let g:go_addtags_transform = "camelcase"

高亮type、function、caller等等

let g:go_highlight_types = 1
let g:go_highlight_fields = 1
let g:go_highlight_fields = 1
let g:go_highlight_function_calls = 1
let g:go_highlight_operators = 1
let g:go_highlight_extra_types = 1
let g:go_highlight_build_constraints = 1
let g:go_highlight_generate_tags = 1

tab转成4空格,默认VIM使用的是8个空格的tab

autocmd BufNewFile,BufRead *.go setlocal noexpandtab tabstop=4 shiftwidth=4

设置配色,注意安装之后,需要将colors/molokai 拷贝到 .vim/目录下,否则找不到配色 colorscheme molokai这句话必须放在plug#begin()plug#end()之外,否则提示找不到这个主题

Plug 'fatih/molokai'
Alternate file

在foo.go 和foo_test.go 文件之间跳转,即在source file 和 test file之间跳转

:GoAlternate
Go to definition

跳转到函数定义的位置

:GoDef
# shortcut
ctrl+] 或者 gd

返回前一个浏览位置,只包括函数跳转

:GoDefPop
# shortcut
ctrl+t

返回前一个浏览位置,包括移动的过程

ctrl+o # 如果执行了gd后又执行了滚屏等操作,或者move操作,则再执行ctrl+o是不会直接跳转到上一级函数的,而是一层层将之前的移动依次回退

前进到前一个位置即ctrl+o的反方向操作

ctrl+i

显示函数调用栈

:GoDefStack

清除调用栈信息

:GoDefStackClear
Move between functions

当我们不知道要查找的函数或者只知道函数名字的部分信息时,可以使用如下命令:

:GoDecls # 显示当前文件中的函数
:GoDeclsDir #显示文件夹中的函数,但是不包括子文件夹

显示如下信息可以进行查找 GoDeclsDir

安装插件ctrlp.vim,说是GoDecls必要插件,但是不装也可以,还是装上吧,毕竟装上之后显示的内容更多

Plug 'ctrlpvim/ctrlp.vim'

快速的函数移动

]] -> jump to next function
[[ -> jump to previous function

设置快捷键GoAlternate

autocmd Filetype go command! -bang A call go#alternate#Switch(<bang>0, 'edit')
autocmd Filetype go command! -bang AV call go#alternate#Switch(<bang>0, 'vsplit')
autocmd Filetype go command! -bang AS call go#alternate#Switch(<bang>0, 'split')
autocmd Filetype go command! -bang AT call go#alternate#Switch(<bang>0, 'tabe')
Understand it
Documentation lookup

获取函数的comment信息,可以通过如下命令:

:GoDoc

GoDoc只是显示光标所在函数的comment信息,并不是文档管理器,如果想要使用文档管理器可以安装go-explorer插件

Identifier resolution

我们在编码时经常需要知道函数的参数和返回值等信息,可以使用如下命令显示

:GoInfo
# shortcut 
autocmd FileType go nmap <Leader>i <Plug>(go-info)
# 自动显示info信息,当光标停留在合法的描述符上后,会自动显示info信息
let g:go_auto_type_info = 1
# 设置停留时间
set updatetime=100
Identifier highlighting

有时候我们需要高亮某个标识符,此时可以使用如下命令:

:GoSameIds

清除高亮

:GoSameIdsClear

自动高亮设置

:GoSameIdsAutoToggle # 临时生效
#或者永久生效配置init.vim
let g:go_auto_sameids = 1
Dependencies and files
:GoFiles
:GoDeps
Guru

Guru是一个导航工具,一般由于查看代码使用。vim-go集成了一部分guru的功能。

guru: https://golang.org/s/using-guru

显示光标所在标识符的所有引用,包括标识符的定义

:GoReferrers

显示光标所在标识符的详细信息, 与GoInfo相似不过信息更多,如果是个结构体,包括结构体定义的方法等都会显示出来

:GoDescribe

显示一个方法对应的实现了那个接口

:GoImplements

显示err可能的值是哪些,如果是自定义的可能没办法获取,go自带的应该可以

:GoWhicherr

显示channel分配,send、recv的信息

:GoChannelPeer

显示调用者信息以及调用栈信息

:GoCallees
:GoCallers
:GoCallstack
Refactor it
Remane

替换光标所在标识符的名称

:GoRename bar

当函数比较复杂的时候,可以通过GoFreeVars抽出部分代码生成一个函数,GoFreevars会根据你的选择判断哪些变量定义是需要作为入参的

# 先选中一段代码,在执行如下
:GoFreevars
# quickfix中显示的内容就是函数需要的入参定义
Generate it

通过快捷方式,实现快速定义某个接口的方法

# 先定义一个struct, 光标移至此struct执行如下命令
:GoImpl
智能补全功能

通过安装coc.vim插件,并配置coc-setting.json文件

{
	"languageserver": {
	  "golang": {
	    "command": "gopls",
	    "rootPatterns": ["go.mod"],
	    "disableWorkspaceFolders": true,
	    "filetypes": ["go"]
	  }
}
}

需要注意的是gopls必须是能够直接被调用的否则coc.vim插件执行gopls命令会失败

显示函数和变量

tagbar

模糊查找工具fzf

fzf

airline 提示文件类型,名称等信息插件

airline

markdown插入剪切板图片

通过nvim编写markdown文章,但是导入图片却是个麻烦事儿,从网上找打vim插件,可以直接一键导入image

"https://github.com/ferrine/md-img-paste.vim
Plug 'ferrine/md-img-paste.vim'
autocmd FileType markdown nmap <buffer><silent> <leader>p :call mdip#MarkdownClipboardImage()<CR>
" there are some defaults for image directory and image name, you can change them
let g:mdip_imgdir = '.'
let g:mdip_imgname = 'image'
let g:mdip_imgdir_absolute = '/{xxx}/Blog/static/images'
let g:mdip_imgdir_intext = '/images'
  • mdip_imgname:表示不输入图片时的默认名称
  • mdip_imgdir:表示输入名称的前缀
  • p:表示插入剪切板的内容到markdown文件中,并提示输入名称
  • mdip_imgdir_absolute: 表示图片保存的绝对路径,由于我的图片存储位置与markdown中输入的内容不同,所以使用相对位置等方式或者让mdip_imgdir即表示markdown中内容又表示文件保存位置是不可行的,所以使用绝对路径和markdown中前缀名称
  • mdip_imgdir_intext: 表示markdown文本中的前缀名称
  • 规则:如果存在absolute,则使用absolute路径代替imgdir路径;如果没有intext路径,则markdown中使用imgdir表示的路径。

参考https://www.cnblogs.com/mazhuang/p/12863702.html

系统剪切板

  • neovim 是不支持与系统剪切板互通的,可以通过如下设置开启
set clipboard+=unnamedplus
# 需要安装剪切工具
# Linux sudo pacman -S xsel
# MacOs brew pbcopy