我经常在不同语言的工程中进行编码,一个通用的轻量级编辑器能明显改善编码体验。多年以前我以vim作为主力编辑器,但是遇到大一点的工程的时候,vim还是略显疲软,各种IDE的辅助功能如代码跳转、结构化视图(symbols) 等有明显优势,因此一度抛弃了vim,只在简单编辑场景下使用。在neovim重塑vim生态,引入lsp之后,代码跳转、补全等功能大大增强,重新将vi(neovim)重新作为日常使用的主力编辑器。
选择vi主要的原因在于它的键位设计让编辑更加高效,对各种编辑操作形成肌肉记忆之后,写程序的思路不会因为大脑切换到如何使用编辑工具而中断。neovim依赖大量的插件来实现更多功能(尤其是围绕lsp的扩展插件)。这些插件通常都有自己设计的键位设置,没有统一的规范,插件之间的键位设计甚至可能存在冲突,这显然不利于”无脑”使用编辑器。因此,设计一套自己常用的键位才能更加高效地使用vi这类的编辑器。
默认键位
normal mode
single Key
| Key | Description |
|---|---|
| a | enter inserting mode |
| A | Go to line end and enter inserting mode |
| b,B | word backward |
| c | delete and enter inserting mode |
| C | delete to the end of line and enter inserting mode |
| d | enter deleting mode |
| D | delete to the end of line |
| e | word-motion |
| E | word-motion(next char) |
| f | forward to the right {char} |
| F | backward to the right {char} |
| g | - |
| G | Go to last line |
| h | Move left |
| H | go to window first line |
| i | enter inserting mode |
| I | repeat inserting mode |
| j | Move down |
| J | join lines |
| k | Move up |
| K | run program under cursor |
| l | Move right |
| L | go to window last line |
| m | mark |
| M | go to the middle line of window |
| n | forward next search |
| N | backward next search |
| o | enter to inserting mode and insert new line below |
| O | enter to inserting mode and insert new line above |
| q | enter recording mode |
| Q | repeat recorded register |
| r | replace one character |
| R | enter replacing mode |
| s | delete one char and enter inserting mode |
| S | delete entire line and enter inserting mode |
| t | like ‘f’, but to the occurrence left |
| T | backword ‘t’, to the occurrence right |
| u | undo |
| U | toggle undo/redo |
| v | visual mode |
| V | linewise visual mode |
| w | word forward |
| W | word forward |
| x | delete char |
| X | backword delete char |
| y | yank |
| Y | yank n lines, like yy |
| z | fold functional prefix |
| Z | double Z do exit?? |
| 0 | go to the first char of the line |
| 1-9 | repeat counts prefix |
| - | lines upward, on the first non-blank character |
| @ | Execute the contents of register {0-9a-z”.=+} count* times. |
| # | backword search |
| $ | go go to the line end |
| % | Find the next item in this line after or under the cursor and jump to its match |
| ^ | To the first non-blank character of the line. |
| & | repeat last substitute in current line |
| * | forward search |
| ( | count sentences backward. exclusive motion. |
| ) | count sentences forward. exclusive motion. |
| { | count paragraphs backward. exclusive motion. |
| } | count paragraphs forward. exclusive motion. |
| \ | unused |
| : | enter command-line mode |
| ; | Repeat latest f, t, F or T count times. |
| ” | select register |
| ’ | Jump to the mark {a-z} in the current buffer. |
| , | unused |
| . | Repeat last change, with count replaced with count. |
| < | Shift {motion} lines one ‘shiftwidth’ leftwards. |
| > | Shift {motion} lines one ‘shiftwidth’ rightwards. |
| ? | Search backward for the [count]’th previous occurrence of {pattern} exclusive. |
| / | Search forward |
single key with ctrl modifier key
| Key | Description |
|---|---|
| a | number increment |
| b | pages Backwards |
| c | Interrupt current (search) command |
| d | Scroll window Downwards in the buffer |
| e | Scroll window lines downwards in the buffer |
| f | Scroll window pages Forwards (downwards) |
| g | Prints the current file name |
| h | Move left (often remap to move to left window) |
| i | backword in jumplist |
| j | Move to bottom window |
| k | Move to up window |
| l | Move to right window |
| m | lines downward, on the first non-blank character linewise |
| n | lines downward linewise |
| o | forward in jumplist |
| p | lines upward linewise. |
| r | Redo |
| s | split window (aften remap to save file) |
| t | backword in tags |
| u | Scroll window Upwards in the buffer |
| v | Start Visual mode blockwise |
| w | Window mode prefix |
| x | Number decrement |
| y | Scroll window count lines upwards in the buffer |
| z | Suspend Nvim, like “:stop” (often used in shell) |
重新设计
neovim 有一篇详尽的 motion文档 来说明各种按键/功能细节,文档中的功能相当多,完全掌握这些按键的不太可能,也没必要。按照日常使用的习惯,我将按键的归位三类:
- 光标移动: 在代码中快速移动到指定位置
- 代码编辑: 快捷编辑代码,如自动提示、触发各种辅助功能
- 全局功能: 模式/窗口切换
所有这些功能首先保留大部分的默认键位,再额外增加一些组件键配置。
光标移动
模式: normal
| Key | Description |
|---|---|
| gs | 基于 hop 的移动 |
| gw | 基于 hop 的word移动 |
代码编辑
编辑代码不像写简单文字那样大多时候是在默认插入或者删除一些文字,而是有更复杂的操作, 如自动完成提示、删除中间的一块代码,增删一对括号,批量重命名等。如果这些功能 只能通过逐个删除/添加字符来实现,那它的效率也太低了。所以我们可以将一些经常使用的功能 用快捷键映射,如”删除整行后进入inserting模式”。
注释
vim默认使用了gc来调用注释配合textobject
motion,可以实现方便的注释操作
| Keys | Description | explain |
|---|---|---|
| gcc | single line comment | |
| gcif | function body block comment | function inner |
| gcaf | function block comment | function outer |
| gcm | on tree-sitter motion comment |
删除
{c} 表示需要输入的字符
| Keys | Description | explain |
|---|---|---|
| dl | delete character (alias: “x”) | dl |
| diw | delete inner word | |
| daw | delete a word | |
| diW | delete inner WORD | |
| daW | delete a WORD | |
| dgn | delete the next search pattern match | |
| dd | delete one line | dd |
| dis | delete inner sentence | |
| das | delete a sentence | |
| dib | delete inner ‘(’ ‘)’ block | |
| dab | delete a ‘(’ ‘)’ block | |
| dip | delete inner paragraph | |
| dap | delete a paragraph | |
| diB | delete inner ‘{’ ‘}’ block | |
| daB | delete a ‘{’ ‘}’ block | |
ds{c} |
删除外围配对的字符 | 基于surround实现,例如如删除包含字符串的引号 |
替换
直接替换类操作比较少,一般使用删除+插入实现,单个字符使用
r 命令。
一个比较高频的操作是 surround replace.
| Keys | Description | explain |
|---|---|---|
cs{c_1} {c_2} |
用{c_2} 字符对替换外围的 {c_1} 字符对 |
|
cS{c_1} {c_2} |
同 cs ,但是额外增加换行 |
text-object 选择
前面的几种操作可以归结为选择text-object后进行删除,使用tress-sitter object + hop 组合后,更容易选择需要的内容。
https://vimdoc.sourceforge.net/htmldoc/motion.html#object-select
| Keys | Description |
|---|---|
gm{c} |
按照语法选择光标周围一块内容 |
全局功能
| Keys | Description |
|---|---|
| Space + f | fuzzy find: project file |
| Space + b | fuzzy find: buffer |
| Space + d | buffer: Delete current |
| Space + q | exit |
| Space + n | buffer: New |
| Space + e | project: file tree explorer |
| Space + z | project: change current root directory |
| Space + o | Open outline window |
| q | Close window (panels) |