起因是使用了很久vscode的插件,但是在前几天看到了一个neovim(以下简称 nvim)的视频。就看自己的vscode不是很顺眼,感觉界面不够简洁,所以尝试使用nvim。这期间尝试了别人的配置和spacevim,但是这会导致两个问题:
- 快捷键自己不熟悉,那么就相当于没有
- 插件不是自己配的,很多东西报错都不知道为什么。
基于这些原因,就决定自己去尝试配置,同时由于nvim中文资料不是很多。特别是比较基础的,大多数只有一个操作的步骤,但是并没有原因或者为什么这样。自己通过阅读一下nvim官网,同时还有nvim用的比较多的包管理插件packer 以及拿来实操的一个插件toggletermj就是在nvim中创建内置的终端。
lua
nvim是通过lua来进行插件管理的,nvim实际运行的是Vimscript,通过提供一些api给lua,从而实现插件管理以及按键映射这些。具体可以看国内的菜鸟教程,主要明白函数就行了。
packer
这里主要讲一下packer的原理。
看一下安装语句
git clone --depth 1 https://github.com/wbthomason/packer.nvim\
~/.local/share/nvim/site/pack/packer/start/packer.nvim
我主要是用的是mac,这里可以看到使用的是一种比较原始的方法,在start目录下放置了一个插件,这里面的插件会在nvim启动时就运行(提醒一下,nvim是从vim中来的,虽然发展这么久很多不一样了,但是很多操作是兼容的,如果nvim查不到中文的,可以试试看vim的相关内容)。
这个插件会暴露出一个packer这么一个对象,通过对packer进行设置就可以安装插件啦。
那么如何给packer传递该管理哪些插件呢?创建一个~/.config/nvim/lua/plugins.lua
然后在里面写上一个返回语句
return require('packer').startup(function(use)
-- Packer can manage itself
use 'wbthomason/packer.nvim'
use {"akinsho/toggleterm.nvim", tag = '*', config = function()
require("toggleterm").setup()
end}
end)
~
这里面就是纯粹的lua语法,解析一下,require("<模块名>")就是调用一个模块,但是这个文件并没有关联其他文件呀,怎么调用的呢。原理下面会说。
然后看一下startup:the action or process of setting something in motion,意思就是开启的意思啦。这里返回一个匿名函数,具体使用的方法就是每个 use后面都接一个插件,接的方法有两种
- A list of plugin specifications (strings or tables)
- A table specifying a single plugin. It must have a plugin location string as its first element, and may additionally have a number of optional keyword elements, shown below:
大概意思就是一个插件的集合和单独一个插件,然后里面具体设定一些东西。然后具体使用就去插件官网看他们的readme就好了,都会有引导。
lua脚本的引入
nvim还是使用~/.config/nvim/init.vim
中来管理(如果没有就创建一个),引入的话就是使用vimscript来调用lua脚本。
这里就是在vim运行时引入上面的lua脚本,在整个vim中就有packer这个模块,因为已经在刚才添加在start这个文件夹下了
-- vimscript脚本
-- ~/.config/nvim/init.vim
" 设置一个leader键,相当于一个自定义的键,我这里设置的空格,可以再接其它键一起设置成新的快捷键,以免出现快捷键的冲突。
let g:mapleader = " "
" 加载上面创建的文件,因为加载的路径会包括~/.config/nvim/lua
lua require('plugins')
" 加载指定lua脚本中指定的function
" command! Scratch lua require(tools).makeScratch()
" 设置快捷键,使用lua
lua require('mapkeyboard')
" 设置快捷键,使用vimscript
" insert terminal
" set
autocmd TermEnter term://*toggleterm#*
\ tnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR>
" By applying the mappings this way you can pass a count to your
" mapping to open a specific window.
" For example: 2<C-t> will open terminal 2
nnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR>
inoremap <silent><c-t> <Esc><Cmd>exe v:count1 . "ToggleTerm"<CR>
这里vimscript没办法高亮,所以md设置的lua。
看上面的代码,具体的用法就已经在上面标注出来了。下面展示一下使用lua设置快捷键的文件。
-- ~/.config/nvim/lua/boardmapping.lua
-- 具体设置的方法
function _G.set_terminal_keymaps()
local opts = {buffer = 0}
-- t表示的是terminal,也就是在使用终端的时候设置下面的快捷键。
-- <cmd> 和: 是相同的意思,表示的就是一个命令
vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts)
vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts)
vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], opts)
vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], opts)
vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], opts)
vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], opts)
end
function _G.set_terminal_keymaps_insert()
vim.keymap.set('n', '<C-h>', [[<Cmd>wincmd h<CR>]], opts)
vim.keymap.set('n', '<C-j>', [[<Cmd>wincmd j<CR>]], opts)
vim.keymap.set('n', '<C-k>', [[<Cmd>wincmd k<CR>]], opts)
vim.keymap.set('n', '<C-l>', [[<Cmd>wincmd l<CR>]], opts)
end
-- 调用设置的方法
-- vim.cmd表示的是执行后面的内容
-- autocmd是表示vim中的自动调用,代表在碰到之后的内容时加载相应的方法.感叹号表示的是避免重复加载
vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
vim.cmd('lua set_terminal_keymaps_insert()')
-- 下面表示的是自定义一个terminal运行相应的命令,这里是在内部终端运行lazygit
local Terminal = require('toggleterm.terminal').Terminal
local lazygit = Terminal:new({
cmd = "lazygit",
dir = "git_dir",
direction = "float",
float_opts = {
border = "double",
},
-- function to run on opening the terminal
on_open = function(term)
vim.cmd("startinsert!")
vim.api.nvim_buf_set_keymap(term.bufnr, "n", "q", "<cmd>close<CR>", {noremap = true, silent = true})
end,
-- function to run on closing the terminal
on_close = function(term)
vim.cmd("startinsert!")
end,
})
function _lazygit_toggle()
lazygit:toggle()
end
vim.api.nvim_set_keymap("n", "<leader>g", "<cmd>lua _lazygit_toggle()<CR>", {noremap = true, silent = true})
实际例子
这里使用的是toggleterm 来进行举例,首先看他的官网。里面说了
Using packer in lua
-- ~/.config/nvim/lua/mapkeyboard.lua
use {"akinsho/toggleterm.nvim", tag = '*', config = function()
require("toggleterm").setup()
end}
直接在~/.config/nvim/lua/plugins.lua
中加上这句话就可以了。具体怎么加,如果不明白就去看上面的,有这个文件的全部内容。
之后往下翻会有一些介绍,和目标什么的。然后就会有一个设置快捷键,这种设置方法一看就是vimscript,图方便就直接在init.vim
中设置了,如果想要简洁,应该可以单独建一个文件夹然后在init.vim
中引用就可以了。
-- vimscript脚本
-- ~/.config/nvim/init.vim
" set
" 在某些情况下,自动运行下面的内容。
autocmd TermEnter term://*toggleterm#*
\ tnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR>
" By applying the mappings this way you can pass a count to your
" mapping to open a specific window.
" For example: 2<C-t> will open terminal 2
nnoremap <silent><c-t> <Cmd>exe v:count1 . "ToggleTerm"<CR>
inoremap <silent><c-t> <Esc><Cmd>exe v:count1 . "ToggleTerm"<CR>
再往下就是一些在~/.config/nvim/lua/plugins.lua
中进行设定的内容
require("toggleterm").setup{
-- size can be a number or function which is passed the current terminal
size = 20 | function(term)
if term.direction == "horizontal" then
return 15
elseif term.direction == "vertical" then
return vim.o.columns * 0.4
end
end,
open_mapping = [[<c-\>]],
on_create = fun(t: Terminal), -- function to run when the terminal is first created
on_open = fun(t: Terminal), -- function to run when the terminal opens
on_close = fun(t: Terminal), -- function to run when the terminal closes
on_stdout = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stdout
on_stderr = fun(t: Terminal, job: number, data: string[], name: string) -- callback for processing output on stderr
on_exit = fun(t: Terminal, job: number, exit_code: number, name: string) -- function to run when terminal process exits
hide_numbers = true, -- hide the number column in toggleterm buffers
shade_filetypes = {},
autochdir = false, -- when neovim changes it current directory the terminal will change it's own when next it's opened
highlights = {
-- highlights which map to a highlight group name and a table of it's values
-- NOTE: this is only a subset of values, any group placed here will be set for the terminal window split
Normal = {
guibg = "<VALUE-HERE>",
},
NormalFloat = {
link = 'Normal'
},
FloatBorder = {
guifg = "<VALUE-HERE>",
guibg = "<VALUE-HERE>",
},
},
shade_terminals = true, -- NOTE: this option takes priority over highlights specified so if you specify Normal highlights you should set this to false
shading_factor = '<number>', -- the degree by which to darken to terminal colour, default: 1 for dark backgrounds, 3 for light
start_in_insert = true,
insert_mappings = true, -- whether or not the open mapping applies in insert mode
terminal_mappings = true, -- whether or not the open mapping applies in the opened terminals
persist_size = true,
persist_mode = true, -- if set to true (default) the previous terminal mode will be remembered
direction = 'vertical' | 'horizontal' | 'tab' | 'float',
close_on_exit = true, -- close the terminal window when the process exits
shell = vim.o.shell, -- change the default shell
auto_scroll = true, -- automatically scroll to the bottom on terminal output
-- This field is only relevant if direction is set to 'float'
float_opts = {
-- The border key is *almost* the same as 'nvim_open_win'
-- see :h nvim_open_win for details on borders however
-- the 'curved' border is a custom border type
-- not natively supported but implemented in this plugin.
border = 'single' | 'double' | 'shadow' | 'curved' | ... other options supported by win open
-- like `size`, width and height can be a number or function which is passed the current terminal
width = <value>,
height = <value>,
winblend = 3,
},
winbar = {
enabled = false,
name_formatter = function(term) -- term: Terminal
return term.name
end
},
}
可以自己看需要进行设置
之后往下就是提供的方法的一些介绍,主要介绍一下通过lua进行快捷键设定。
这里因为是lua脚本,上面的plugins.lua上来就直接返回了。同时为了保证降低耦合,就没有尝试继续在里面加内容。就新建了一个lua脚本文件。~/.config/nvim/lua/mapkeyboard.lua
这个文件,按照它给的内容
这里vim就是暴露出的一个全局变量,通过设置vim就可以对编辑器的特性进行设置
-- ~/.config/nvim/lua/mapkeyboard.lua
function _G.set_terminal_keymaps()
local opts = {buffer = 0}
vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts)
vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts)
vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], opts)
vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], opts)
vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], opts)
vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], opts)
end
-- if you only want these mappings for toggle term use term://*toggleterm#* instead
vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
创建好后再再init.vim这个文件中加上对这个文件的引用。
lua require('mapkeyboard')
这个同样也在上面init.vim中展示了全部的设置。
这个快捷键的大致意思就是:通过control加上h,j,k,l实现在terminal(内置终端)情况下也能跳文档编辑继续进行编辑。我觉得跳回文档编辑后不能再跳回去,就不太舒服又加了几个在normal(vim的编辑模式)中能运行的快捷键,其实实际含义就是输入:wincmd k/j/h/l
进行跳转。
所以最后就是下面这个样子
-- ~/.config/nvim/lua/boardmapping.lua
-- 具体设置的方法
function _G.set_terminal_keymaps()
local opts = {buffer = 0}
-- t表示的是terminal,也就是在使用终端的时候设置下面的快捷键。
-- <cmd> 和: 是相同的意思,表示的就是一个命令
vim.keymap.set('t', '<esc>', [[<C-\><C-n>]], opts)
vim.keymap.set('t', 'jk', [[<C-\><C-n>]], opts)
vim.keymap.set('t', '<C-h>', [[<Cmd>wincmd h<CR>]], opts)
vim.keymap.set('t', '<C-j>', [[<Cmd>wincmd j<CR>]], opts)
vim.keymap.set('t', '<C-k>', [[<Cmd>wincmd k<CR>]], opts)
vim.keymap.set('t', '<C-l>', [[<Cmd>wincmd l<CR>]], opts)
end
function _G.set_terminal_keymaps_insert()
vim.keymap.set('n', '<C-h>', [[<Cmd>wincmd h<CR>]], opts)
vim.keymap.set('n', '<C-j>', [[<Cmd>wincmd j<CR>]], opts)
vim.keymap.set('n', '<C-k>', [[<Cmd>wincmd k<CR>]], opts)
vim.keymap.set('n', '<C-l>', [[<Cmd>wincmd l<CR>]], opts)
end
-- 调用设置的方法
-- vim.cmd表示的是执行后面的内容
-- autocmd是表示vim中的自动调用,代表在碰到之后的内容时加载相应的方法.感叹号表示的是避免重复加载
vim.cmd('autocmd! TermOpen term://* lua set_terminal_keymaps()')
vim.cmd('lua set_terminal_keymaps_insert()')
总结
原理
主要就是串了一下nvim的插件管理。
- nvim使用的还是vimscript脚本,只是兼容了lua。
- 可以通过在
~/.local/share/nvim/site/pack/packer/start/
中安装插件直接在运行时就导入插件 ~/.config/nvim/init.vim
是一个vimscript脚本,里面可以设置要执行的lua脚本,和对vim/nvim进行配置等
基于上面的原理,首先通过在start中安装packer直接导入packer这个插件进行插件管理,之后通过init.vim给packer提供需要管理插件的各种信息,从而实现插件的管理。
实际操作步骤
- 去对应插件官网看如何在plugins.lua中加载包,复制粘贴过来
- 在对应插件官网看可以设置哪些,如果是直接对插件操作一般就是在plugins.lua包中对应插件的set()中进行设置
- 如果是快捷键
- 在init.vim中直接使用vimscript脚本进行设置
- 通过lua进行设置,然后在init.vim中导入lua脚本
问题
- 对于packer具体实现和vimscript的了解还是不深入,后面随着具体的使用再慢慢学习。