⟵ 春水煎茶 · 王超的个人博客

我的命令行开发环境 ❤️

常言道,工欲善其事、必先利其器,熟练一套高效且趁手的工具,对开发工作的重要性不言而喻。

本文分享我的命令行开发环境,文章较长,配图较多, 其中几处详细内容,已将其折叠起来,读者可视兴趣展开阅读。

命令行工具非常多,称得上神器的,提炼为以下四个:

我选择工具的标准是:美、快、爽

终端程序 — Alacritty

Alacritty 是一款 rust 编写的、 启用 GPU 加速的终端模拟器。

天下武功,唯快不破,它唯一特点就是:快。

图1.1 alacritty 终端模拟器

在 2018 年之前,我一直在用流行的 iterm 2。 不过,自从邂逅 alacritty 后,就从未离开。相比 iterm2 来说,alacritty 的功能要简单的多, 但是,它足够快。

下面的动图展示了同时在 alacritty(左) 和 iterm2 中用 viu 打开一张图片时的渲染速度:

图1.2 左边 alacritty 和 右边 iterm2 渲染图片对比

作为终端,一定要颜值在线。 Alacritty 也支持配色,可参考 alacritty-theme , 我用的配色是 snazzy ,从未换过。 此外,选择一款 等宽的 字体,对终端的整体颜值至关重要,我一直在用的字体是 InconsolataLGC Nerd Font。

我对 alacritty 的功能需求非常少,只要它够快,够漂亮 就好。比如说,我是没有用终端 Tab 页的习惯的, 这些事情 tmux 完全可以搞定。作为终端模拟器,简单快速就好!

Shell – Fish

无独有偶,选择 fish shell 的原因,仍然是它更快,同时也更轻。

这里所说的「快」,是指 shell 的 prompt 加载快, 如下图中,进入 fish 几乎是没有延迟的:

图2.1 配置的少的 shell 才快

当然,不带任何配置的 zsh 和 bash 也很快。 但是,为了酷炫的颜值、快捷的功能, 许多 zsh 用户都会搭配类似 oh-my-zsh 的东西, 或者是搞一大堆配置,这都会让 shell 变慢。

但是真正的不同在于,fish 是开箱即用的,大可不必自己折腾那么多。 比如 fish 的一大特色是自带补全暗示功能:

图2.2 fish 自带的 hint 提示

还有自带的 ManPage 补全功能等等,这一切都无需插件。

图2.3 fish 自带的 ManPage 补全功能

因此 fish 的插件要相对少得多。我在用的也很少:

  • pure-fish/pure 从未换过的 fish prompt 主题,它是如此简洁。
  • jethrokuan/z 快速跳转目录的插件

    图2.4 fish shell 中 jethrokuan/z 的自动跳转

除去工具本身外,unix 中 emacs 风格的 shell 快捷键 也值得推荐, 它们在常见的 shell (fish, zsh, bash) 中都是通用的,其中部分快捷键甚至可以在网页文本框中使用。

Emacs 风格的 shell 快捷键 - 光标移动和编辑
  • C-a 光标回到行首, C-e 光标回到行尾
  • C-f 光标向前移动一个字符,C-b 光标向后移动一个字符
  • Alt-f 光标向前移动一个单词, Alt-b 光标向后移动一个单词
  • C-k 清掉光标后面的部分, C-d 删掉光标后的一个字符 或 退出
  • C-l 相当于 clear 命令,清屏
图2.5 unix 默认的 emacs 风格快捷键

甚至,当命令非常长的时候,还可以跳入编辑器来编辑命令 ( fish 配置 ):

图2.7 调用编辑器来编辑长命令

Emacs 风格的 shell 快捷键 - 回翻历史命令
  • C-p 向上翻历史命令, C-n 向下翻历史命令
  • C-r 向上搜索历史命令 (顺手推荐搜索命令历史的增强工具 fzf)
图2.6 FZF C-R 搜索历史命令

Emacs 风格的 shell 快捷键 - 进程挂起
  • C-z 挂起当前进程到后台
  • fg 以恢复到前台
图2.6.1 挂起当前进程到后台

其实,主流的 shell (fish, zsh, bash) 也都支持 vi 风格的操作模式。 虽然身为 vim 用户,之所以在 shell 中选择 emacs 风格,是因为它在大部分情况下都是 默认的 设定。

在带过的每一支技术团队中,我总会提醒小伙伴们要勤用这些 emacs 风格的快捷键,它看似麻烦, 但是一旦熟练起来,一定比鼠标快。只要刻意地去用,就会熟起来,将大有裨益。

终端复用 — Tmux

Tmux 是一款终端复用神器,不必多言,它早已声名远扬。

简单讲,终端复用就是在一个终端中,可以开多个 shell 会话。 一些现代的终端应用,比如 iterm2, 也支持 Tab 页 和 切屏,不过它们在 tmux 的颜值和效率面前,都相形见绌。

图3.1 tmux 常用的功能

几乎所有的 tmux 功能,都需要按一下 前缀键 来触发, 默认的是 C-b,我则是习惯用 C-a

我常用的 tmux 功能有:面板、窗口、Copy 模式、会话保持 还有 两三个小插件。

tmux - 面板和窗口快捷键
  • C-a " 横向打开一个面板
  • C-a % 纵向打开一个面板
  • C-a o 在面板之间切换
  • C-a q 展示当前窗口的所有面板的标号
  • C-a z 放大或缩小一个窗口
  • C-a c 创建一个窗口
  • C-a n 切换下一个窗口
  • C-a p 切换上一个窗口

作为一枚 vim 党,我习惯用 hjkl 来切换面板,例如 C-a h 会跳入右边的面板、C-a j 会跳入下边的面板 等 ( 相关 tmux 配置 )。

tmux - copy 模式

刚进入 tmux 的同学,常会遇到一个问题:如何滚屏 ?

在 tmux 中可以 C-a [ 来进入一个叫做 copy mode 的模式, 然后就可以用方向键滚屏了。 但是方向键太难用了,可以设定 copy mode 下采用 vim 键位 ( 相关配置 )。

在 copy 模式下,vim 键位可谓如鱼得水:

  • C-uC-d 来上下翻页
  • hjkl 来上下左右移动光标
  • 0 来跳到行首、$ 来跳到行尾
  • w, e, b 来按单词为单位前后移动光标
  • fF 来前后搜索字符以跳跃光标
  • v 开始选择一块区域,V 开始按行选择一块区域
  • y 以拷贝内容,并退出 copy 模式, q 也可退出 copy 模式
图3.2 tmux 的 copy 模式

还可以在 copy 模式下调整面板大小, 仍然采用 vim 风格的 C-hjkl ,是不是很有趣? (相关配置)

图3.3 tmux 的 copy 模式下调整面板的大小

tmux - 会话保持

Tmux 还有一个非常棒的功能,就是会话保持。简单来说,就是可以暂时保存工作环境,稍后可重新进入。 这个功能对于需要服务器跑脚本这种事情,非常合适,不必再担心 ssh 断掉后进程断掉。

  • C-a d 临时离开当前 tmux 会话 (detach 之意)
  • tmux a 重新进入 tmux 会话 (attach 之意)

甚至如果把整个终端模拟器退出,仍然可以重新进去,工作环境不会丢失。 当我的 alacritty 出现讨厌的闪屏之类的小问题时,我会直接 Cmd-q 杀掉终端,重启终端后即可直接回到 tmux 中。 前面的 图1.2 就是在两个不同的终端应用中运行同一个 tmux 会话,两边的交互完全是同步进行的。

图3.4 tmux 的会话临时退出和恢复

tmux - 用到的三个小插件

Tmux 拥有着 丰富的社区生态, 其中不乏一些 “花里胡哨” 的插件,我不怎么用,状态栏比较干净。 比较喜欢的插件则是这三个:

Tmux 的可配置性很强,我的建议是,多用默认的快捷键,只做少许的自定义快捷键, 毕竟我们许多时候会在服务器等其他环境下使用它。

编辑器 — Vim

Vim 有着「编辑器之神」的美誉, 如果把程序员比作武林中的侠客,那么 vim 就是倚天屠龙的利器。 已诞生 30 余年, 宝刀虽老,但是在 2022 年的今天,它的文本编辑效率仍尚无敌手

图4.1 我的 vim 编辑器的样子

相对于现代编辑器 或 IDE 来说,上手 vim 的难度曲线要陡峭许多。 据说有许多新手在刚刚进入 vim 时,不得不关机或重启终端程序来退出它 …

不过,这是个先苦后甜的过程。 如果前面花功夫去学,坚持用下去,甚至形成肌肉记忆,就会爱上它,而 vim 也会在后期带来惊喜。 熟练之后,自会入境,享受 「指随心动、码字如飞」的感觉。

Vim 中的学问很多,我用 vim 码字也蛮多年了,但也远远不敢妄称精通,只能说是经常用。

但是,只需要了解 vim 中少部分的知识点,就足够完成大部分的编辑工作

我将基本内容总结为下面几块。

Vim - 5 种常用的模式
  • Normal 普通模式,EscC-c 进入
  • Visual 可视模式,v, V, C-v 等键进入
  • Insert 插入模式,i, I, a, A, o, O 等键进入
  • Command 命令模式,: 进入,常用的有: :w, :q, :wq, :%s, q:
  • Replace 替换模式,R 进入

普通模式是最常用的模式,其他模式都要从普通模式进入。

可以设置 set showmode 来显示当前的模式,此外也可以采用 lightline 插件, 可以在状态栏看到当前的 vim 模式和其他信息。

图4.1.1 lightline 插件效果图

Vim - 快速移动光标 motion
  • 字符粒度:h j k l 左、下、上、右
  • 单词粒度:w W e E 向前,b B 向后
  • 行内移动:0 行首,$ 行尾,f{c} 向前搜字符、F{c} 向后搜字符 c
  • 按块移动:( ) 句子、{ } 段落 等
  • 翻页:C-u 向上翻页,C-d 向下翻页
  • 全文:文首 gg,文尾 G ,等价于 :0:$

这些键位,最好结合其英文含义来理解, 比如说 w 是 “word” 的意思,e 是 “end” 的意思,b 是 “backward” 的意思, f 是 “find” 的意思,C-u 是 “up” 的意思 等等。

悄悄地说,我其实并未真正去记 hjkl 到底哪个向上、哪个向下,但是手熟了之后, 手放在键盘上自然就可以运用起来。这和许多人背不过键盘布局,但是仍然可以熟练打字是一样的。 我认为 vim 的键位更多地依赖于熟练度,而不是死记硬背

我比较常用 f 跳转,有一款加强 vim 的 f 效果的插件,叫做 quick-scope, 它会自动高亮一些字符,方便 f 去跳转,小巧有趣。

图4.3.1.1 vim 插件 quick-scope 高亮辅助 f 跳转效果图

对于行内跳转光标,有一款插件叫做 vim-ship , 它以 二分的形式 移动光标,s 向前移动到一半,S 向后移动一半, 是不是很聪明!正是由于这个插件,我经常使用 s 来二分跳进光标,可以很快地靠近编辑目标附近。

图4.3.1.2 vim 插件 vim-skip 的二分跳跃效果

这些在 vim 中都叫做 motion, 它的重要之处不仅仅在于光标移动本身,还可以 和数字、文本编辑、visual 模式相结合,举例来说:

  • 可以 4j 来直接向下移动 4 行、4e 来向前跳 4 个单词 等
  • 可以用 motion 来帮助选择文本块,比如 vaw 会选中一个单词、 v 进入 visual 模式后可以 hl 左右调整区域范围 等等

    图4.3.2.1 vim 中 单词粒度 motion 向左向右选中一块区域

    图4.3.2.2 vim 中 motion 结合 C-v 调整一块区域的选择

  • 结合文本编辑,例如:de 可以删除当前位置到单词尾巴的内容,c$ 可以清除当前位置到行尾的内容, c0 可以清除到行首的内容等。

    图4.3.3.1 vim 中 de 和 d$ 的效果

    有一类 motion 叫做 文本对象 text objects, 例如 ciw 可以编辑一个单词 等等。

    图4.3.3.1 vim 文本对象 - ciw 重新编辑一个单词的实例

    有一款插件叫做 targets.vim ,它极大地扩充了可用的文本对象。 比如说 vi" 可选中双引号之内的内容,di) 可删除括号内的内容等等。

    图4.3.3.2 vim 文本对象 targets.vim 插件- vi" 和 vi) 的效果

我使用 vim-skip 的 s 二分跳进 和 0 $ 的频率是很高的, 因为它们可以在行内 大幅度地跳转光标。 当光标靠近想要编辑的文本时,才开始采用小粒度移动方式(按字符或者单词)。 我也几乎不用段落跳转 {},而是直接使用翻页 C-u/d,并不是说后者更好,而是说习惯一种就好。 人总是很难记住很多东西的,更多的情况是,用起来一些顺手了,就会经常用它们

Vim - 快速编辑文本

常用编辑快键位:

  • a 向右插入,i 向左插入,o 向下插入,O 向上插入
  • x 删除一个字符,d 删除选中文本,dd 删除一行
  • r 替换当前字符, R 进入替换模式
  • u 撤销最近一次编辑
  • J 把下一行提上来,合到当前行
  • c 清空重新编辑、cc 清空当前行
  • > 向右递进、< 向左递进, >><< 缩进一行
  • y 拷贝选中区域,yy 拷贝一行,p 粘贴

可以发现,对于一些操作,连续两次即可操作一行,例如 dd, cc, >>, yy 等:

图4.4.1.1 Vim 中 dd, >>, cc 的操作效果

其中,一些编辑操作是可以结合数字使用,例如 4x 可以连续删除 4 个字符。 也可以结合前面所说的 motion ,例如 c$ 可以清掉到行尾的内容。 还可以结合 visual mode,例如选中一块区域后 > 来缩进 等等,不再展开。

图4.4.1.2 Vim 中缩进一块选中区域

Vim 中也可以对多行编辑:

  • 比如要批量注释一段代码,可以 C-v 进入 visual mode ,然后 I 插入注释字符:

    图4.4.2.1 Vim 中 C-v 编辑多行,操作: C-v I ESC

    单单对于注释多行来说,有一个更便捷的插件,叫做 vim-commentary, 可以直接选中多行,然后按 \\ 就可以了 ( 相关key mapping 配置 )

    图4.4.2.2 vim-commentary 快速注释插件

  • 也可以把多行聚合成一行,C-v 选中多行,然后 J 即可:

    图4.4.2.3 Vim 中聚合多行为一行,操作:C-v J

  • 说到多行编辑,vim 的世界中有一个出名的插件叫做 vim-multiple-cursors ,不过它已经年久失修, 而且性能贼差。我在用的是 vim-visual-multi,还是很快的。 它的一个优点在于,我们可以在多行编辑时运用 vim motion,比如 w e b f 等。

    图4.4.2.4 vim-visual-multi 编辑多行的效果,操作: C-n tab fi a ESC

另一类常见的编辑需求,是搜索和替换:

  • /{text} 当前文档内向后搜索文本,?{text} 向前搜索文本
  • n 以继续下一个结果,N 以回到上一个搜索结果
  • :%s/a/b/ 当前文档内全局替换 ab

默认地,在 vim 中搜索文本,是没有高亮的,可以设置 set hlsearch 。 有一个叫做 traces.vim 的插件, 它可以实时地高亮搜索命中的部分。

图4.4.3.1 vim-traces 实时显示搜索和替换效果

Vim - 窗口和标签页

Vim 也支持切屏和标签页,常用的操作有:

  • :spl 横向切屏、:vsp 纵向切屏
  • C-w hjkl 来跳到不同窗口
  • :tabnew 新建标签页
  • gtgT 来切换标签页

我平时用切屏比较多,因为写代码时,经常需要一边写这里、一边写那里。

推荐两个插件:

  • winresizer - 用 hjkl 的方式更方便地调整 vim 窗口的大小 (和 tmux 中类似)
  • zoomwintab.vim - 放大和缩小 vim 窗口 (和 shell 中的 C-z, tmux 中的 z 类似, 我也采用 z 作为快捷键)
图4.5.1 vim 窗口大小调整和缩放效果

Vim - 开发和编程

大部分情况下,vim 都是用来做编程开发,在这一方面,它的表现丝毫不比流行的 IDE 差:

  • 语法高亮

    所有主流的编程语言,都有对 vim 的高亮的插件。对我而言,可能唯一的问题是, 有些编程语言的插件做的太臃肿了,它们提供一揽子配套,比如 vim-gopython-mode 等等, 但我一般只需要语法高亮,并不想去学习它们额外的一些命令、键位什么的,因为这些都不通用。 针对这种 “绑上编程语言 IDE 式” 的插件,我要么找更轻量的替代品,要么就 fork 出来,只扒出来语法高亮的部分用。

    对于 nvim 来说,最好的代码高亮方案无疑是 nvim-treesitter, 它提供了一揽子式的、飞快的、增量式的语法高亮能力, 再也不用对每一个语言装一个基于正则的语法高亮插件了。

  • Git 集成

    我没有在用 vim-fugitive 之类的插件, 因为我觉得这些事情都可以在 shell 中完成,也不必为用一个插件而单独去学习新的命令。

    在用的一个插件叫做 vim-signify , 它可以显示出改了哪些行。

    图4.6.1 vim-signify 显示修改的 diff 情况

    另一款在用的插件叫做 diffview ,它可以让我们一览所有改动的情况, 简单说,就是相比 vimdiff 多了导航栏。我一般用在 code review 中。这是一款仅支持 neovim 的插件。

    图4.6.2 nvim 中打开 diffview 的效果

    查看当前文件的 blame 信息, 我是直接采用 tig ,不大喜欢 nvim-blame-line 之类的插件,它们以 VirtualText 形式展示当前行 blame 信息。 而是喜欢 tig blame 这种可以看到当前文件所有行的最新 commit 信息,并且可以跳进去看具体改动。

    vim 和 nvim 中的 tig blame
    • 在 vim 中调用 :!tig blame % 即可
    • 在 nvim 中则是 :terminal tig blame %
    图4.6.3 nvim 中调用 tig 查看 blame 信息

  • 缩进和不可见字符

    对于写 Python 的人而言,显示缩进的层级可能非常重要,推荐 indentLine , 在 nvim 下的话则推荐 indent-blankline.nvim

    图4.6.3.1 vim-indentline 在 Python 代码中的效果

    事实上,其实我们也不需要这种显示缩进的插件,设置 listchars 是更轻量的无插件方案:

    vim/nvim 中设置不可见字符
    " 显示不可见字符
    set list
    " 设置不可见字符的展示字符
    set listchars=space:·,tab:▸\ ,eol:¬,extends:❯,precedes:❮
    
    图4.6.3.2 设置不可见字符展示后,在 Python 代码中的效果

    图4.6.3.3 设置不可见字符展示后,在 Golang 代码中的效果

    此外,对于大部分编程语言,都不希望末尾带多余空白,虽然我们可以设置 eol 行尾符的可见展示方式,但不够显眼, 不如直接红色高亮行尾空白。

    vim/nvim 中配置高亮行尾多余空白和清理
    function! HighlightExtraWhitespace()
      highlight ExtraWhitespace ctermbg=red guibg=#EC7063
      match ExtraWhitespace /\s\+$/
    endfunction
    
    autocmd BufWinEnter * if &buftype != 'terminal' | call HighlightExtraWhitespace() | endif
    
    "保存时自动清理行尾空白
    :command WS :%s/\s\+$//e
    autocmd BufWrite * :WS
    
    图4.6.3.4 高亮末尾多余空白

  • 补全和定义跳转

    补全和定义跳转,是对编程效率至关重要的一环。我在用的是 coc.nvim , 它支持大部分的编程语言, 速度上不快不慢,够用,大部分情况都表现良好。

    coc 中,补全列表中的条目浏览采用 C-pC-n ,也符合 shell 中的快捷键风格。 定义跳转,我用 gd 来映射。

    在编程中,相信和大家一样,我是非常依赖补全和定义跳转的

    图4.6.4.1 coc.nvim 的补全效果

    2022 年 10 月更新: 目前我已经切换到 nvim-cmp ,感觉上更轻更快, 但是仍然很佩服 coc.nvim 的成熟度和美好体验,coc 做的很友好、很用心。

    图4.6.4.2 nvim-cmp 的补全效果 和 gd 跳转定义

  • 格式检查

    许多编程语言都有一套约定俗成的风格和检查工具,比如 black 之于 Python,clang-format 之于 C/C++, gofmt 之于 Golang 等等。Vim 中也有支持格式检查的插件,比如 ale , 它可以在编辑保存时,自动格式化代码,目前我用上去没觉得卡。

    图4.6.5.1 ale 自动格式化代码

顺带一提,目前在用的配色是 PaperColor ,用了好久了, 中间偶尔会烦一下,换换配色、换换心情,不过,最终又总是回到这个配色上来。

Vim 的键位集中在字母区,大部分情况下,在 vim 中编辑,手是不用离开键盘的。 话说,是不是许多 HHKB 用户都喜欢用 vim 呢?

如果非要去理解 vim 编辑器之高效到底来自何处,恐怕只能亲身体会才能知晓。我看来,高效的不是 vim 本身, 而是它帮用户养成的编辑习惯。

目前有一款社区维护的、脱胎于 vim 的编辑器,叫做 NeoVim , 它支持 lua 语言的插件, 对 LSP 作了 内建支持, 而且完全兼容 vim ,在社区驱动下,neovim 的迭代要比 vim 快多了,社区生态也是遍地开花。 不过我仍然没有离开 vim8,因为目前为止还找不到什么动力去换,或许未来会尝试切过去试一段时间, 所谓, 生命不息,折腾不止 嘛。

2022 年 10 月 3 日 更新:

目前我已完全切到了 NeoVim 上, 尽管 vim 9 已经发布 , 但是我认为 nvim 的迭代速度更快、更开放,未来可期。

  • 补全插件我切换到了 nvim-cmp, 体感上比 coc.nvim 更轻快。
  • 语法高亮都切到了 nvim-treesitter

结尾语


命令行下的快捷键虽然多,但是不同工具的快捷键,都具有一定的共通性。 而这些快捷键熟练之后,将体会到,键盘操作是比鼠标要快的。

在配置方面,许多人都把配置做成了仓库托管在 github 上 , 我的在这里 - hit9/dotfiles

最后,天下神器,因人而异。不同的工具在不同的人手上、发挥的效果不尽相同。 工具的强大,因为它们的高上限,而最终威力几何,取决于执剑之人

神兵之神,不在兵器本身,而在于用兵器的人。

(完)

王超 ·
评论 · 首页 · 订阅