键盘效率提升指南(VSCode+Vim+SurfingKeys)

不少编程大佬推崇 纯键盘工作流。熟练掌握各种快捷键和操作模式后,手指几乎不需要离开键盘,就能完成绝大部分编码工作,极大提升编码效率。

需要强调的是,纯键盘操作并不意味着完全摒弃鼠标。不同任务场景有不同的最优操作方式,关键是根据具体情况选择最合适的工具。我们的目标是建立一套平衡且高效的工作流程,让技术为生产力服务,而不是成为负担。无需强迫做一个看起来很厉害的纯键盘党。

本文主要从笔者实践角度出发,介绍几个关键的键盘效率提升方案:

  • VSCode 编辑器:快捷键配置和 Vim 模式使用
  • 浏览器导航:通过扩展实现键盘控制网页浏览
  • 辅助软件:键位映射、输入法切换、窗口管理等

本文以 macOS 系统为准,相关配置和软件说明均在 keyboard-master 仓库。

Karabiner-Elements

Karabiner-Elements 是一款非常强大的键位修改软件。除了简单的键位映射,也支持 Complex Modifications 来处理复杂的键位映射。

配置高级映射比较复杂,所幸 Karabiner-Elements 内置了一些规则,如果内置规则不满足,也可以通过 Karabiner-Elements complex_modifications rules 获取别人分享的好用规则。

笔者使用的规则就两条:

  1. Caps_Lock 键映射为 Escape 键和 Hyper 键 (即 Shift+Command+Option+Control

    • 单独按下 Caps_Lock 时,等同于按下 Escape 键,手指不用跑那么远
    • 按下 Caps_Lock 加上任意其他按键时,等同于按下 Shift+Command+Option+Control 加其他按键,在自定义 VSCode 快捷键时很有用,避免键位冲突,且按起来方便
  2. Caps_Lock 加上 i/j/k/l 映射为方向键

    • 即 Vim 的移动方式,手指不用离开主键盘区域
    • 在各种软件的下拉选项中使用尤其方便

VSCode

快捷键

除了常规快捷键设置外,VSCode 也提供了基于条件(when 属性)的快捷键设置,以及 runCommands 命令,可以通过它一次性执行多个操作。

when 的好处在于可以使用同一套快捷键,搭配不同条件实现不同场景的操作,减少设置多个快捷键位的麻烦。

比如,VSCode 设置可以是图形界面,也可以是 settings.json 配置文件。键盘快捷键设置也同理,既有图形页面,也有 keybindings.json 配置文件。通过 when 就可以实现快速切换两个界面:

json 复制代码
[
  // 打开键盘快捷键设置面板
  {
    "command": "workbench.action.openGlobalKeybindings",
    "key": "cmd+k cmd+s"
  },

  // 在键盘快捷键设置面板中时,打开键盘快捷键 JSON 文件
  {
    "command": "workbench.action.openGlobalKeybindingsFile",
    "key": "cmd+k cmd+s",
    // 仅在键盘快捷键设置面板中生效
    "when": "inKeybindings"
  },

  // 打开设置面板
  {
    "command": "workbench.action.openSettings",
    "key": "cmd+,"
  },

  // 在设置面板中时,打开设置 JSON 文件
  {
    "command": "workbench.action.openSettingsJson",
    "key": "cmd+,",
    // 仅在设置编辑器中生效
    "when": "inSettingsEditor"
  }
]

when 的可配置条件非常多,建议仔细阅读 when clause contexts 官方文档,如果有些很细致的条件没有注明,文档最后也提到了如何通过 调试查看 context keys 信息

简化文件操作

当我们处理资源管理器界面时,同样可以通过 when 简化创建文件、创建文件夹、重命名等操作。

json 复制代码
[
  // 新建文件(按键 A)
  {
    "command": "explorer.newFile",
    "key": "a",
    // 仅在文件资源管理器有焦点且没有输入焦点时生效
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 新建文件夹(按键 F)
  {
    "command": "explorer.newFolder",
    "key": "f",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 重命名文件(按键 R)
  {
    "command": "renameFile",
    "key": "r",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 复制文件(按键 Y)
  {
    "command": "filesExplorer.copy",
    "key": "y",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 剪切文件(按键 X)
  {
    "command": "filesExplorer.cut",
    "key": "x",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 粘贴文件(按键 P)
  {
    "command": "filesExplorer.paste",
    "key": "p",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 删除文件(按键 D)
  {
    "command": "deleteFile",
    "key": "d",
    "when": "filesExplorerFocus && !inputFocus"
  },

  // 复制相对文件路径(Ctrl+Shift+Alt+Cmd+C)
  // 这里用到了 Karabiner-Elements 的 Hyper 组合键
  {
    "command": "copyRelativeFilePath",
    "key": "ctrl+shift+alt+cmd+c"
  },

  // 折叠资源管理器文件夹(双击 Z)
  {
    "command": "workbench.files.action.collapseExplorerFolders",
    "key": "z z",
    "when": "filesExplorerFocus && !inputFocus"
  }
]

简化全局搜索

全局搜索后,想快速定位到搜索结果,之前需要靠鼠标点击,现在可以通过确定后,直接使用 Tab 跳到搜索结果,如果此时想快速再回到搜索框,可以直接使用 Shift + Tab(当然也可以再按全局搜索快捷键):

json 复制代码
[
  // 聚焦搜索结果列表(Tab)
  {
    "command": "search.action.focusSearchList",
    "key": "tab",
    // 仅在搜索视图有焦点时生效
    "when": "searchViewletFocus"
  },

  // 返回搜索输入框(Shift+Tab)
  // 没有直接 focus search input 的命令,所以采用了 runCommands,然后多次调用 previousInputBox 把焦点往前移动
  // 最多只会存在 4 个输入框(搜索,替换,包含文件,排除文件),所以移动 4 次肯定能保证回到搜索框
  {
    "args": {
      "commands": [
        "search.focus.previousInputBox",
        "search.focus.previousInputBox",
        "search.focus.previousInputBox",
        "search.focus.previousInputBox"
      ]
    },
    "command": "runCommands",
    "key": "shift+tab",
    // 仅在有搜索结果且搜索视图有焦点时生效
    "when": "hasSearchResult && searchViewletFocus"
  }
]

简化 Git 打开文件操作

如果安装了 GitLens 插件,通过 GitLens 打开的历史版本和通过 VSCode 自带代码管理器打开的文件 resourceScheme 会不同,并且打开文件背后所使用的命令也不同:

可以通过 Command + O 来统一通过历史版本直接打开对应的工作文件:

json 复制代码
[
  {
    "key": "cmd+o",
    "command": "runCommands",
    "args": {
      "commands": ["git.openFile", "gitlens.openWorkingFile"]
    },
    // 仅在 Git 相关的文件视图中且不在多文件比较编辑器中时生效
    "when": "(resourceScheme =='gitlens' || resourceScheme == 'git' || scmProvider == 'git') && activeEditor != 'multiDiffEditor'"
  },

  // 关闭所有 Git 差异编辑器(Cmd+K Cmd+D)
  {
    "key": "cmd+k cmd+d",
    "command": "git.closeAllDiffEditors"
  },

  // 禁用系统的打开文件操作,对我来说没啥用
  {
    "key": "cmd+o",
    "command": "-workbench.action.files.openFileFolder",
    "when": "isMacNative && openFolderWorkspaceSupport"
  }
]

简化 Terminal 操作

VSCode 自带的打开 Terminal 的方式对手指不太好,将其改为 Ctrl + \,并且支持在聚焦在 Terminal 中,通过 Command + W 关闭终端,和 Ctrl + M 最大化:

json 复制代码
[
  // 切换终端显示状态(Ctrl+\)
  {
    "command": "workbench.action.terminal.toggleTerminal",
    "key": "ctrl+\\",
    // 仅在终端处于活动状态时生效
    "when": "terminal.active"
  },

  // 新建终端(Ctrl+Shift+\)
  {
    "command": "workbench.action.terminal.new",
    "key": "ctrl+shift+\\",
    // 仅在终端处于活动状态时生效
    "when": "terminal.active"
  },

  // 关闭终端(Cmd+W)
  {
    "command": "workbench.action.terminal.kill",
    "key": "cmd+w",
    // 仅在终端可见且终端有焦点时生效
    "when": "terminal.visible && terminalFocus"
  },

  // 分割终端(Ctrl+Shift+Alt+Cmd+\)
  {
    "command": "workbench.action.terminal.split",
    "key": "ctrl+shift+alt+cmd+\\",
    // 仅在终端有焦点时生效
    "when": "terminalFocus"
  },

  // 切换面板最大化状态(Ctrl+M)
  {
    "command": "workbench.action.toggleMaximizedPanel",
    "key": "ctrl+m",
    // 仅在终端有焦点时生效
    "when": "terminalFocus"
  }
]

Tips:如何知道这些 command 的 ID 呢?在键盘快捷方式中,选择一个命令右键复制命令 ID 即可。

Vim 模式

接下来来到了本文的重头戏:Vim 模式。

学习 Vim

本篇文章不是 Vim 使用教程,对于非 Vim 用户,通过 Learn Vim 这个插件来快速学习最核心的一些操作:

Vim Cheatsheet 插件是 Vim 按键的说明:

如果安装 Vim 的同学,也可以直接通过 vimtutor 学习:

学习 Vim 比较重要的一点就是 "刻意练习",尤其是一开始接触时。笔者也是老听一些大佬吹 Vim 的强大,尝试学习了好几次,也能确实感觉到一些操作的便捷性,但最终也没有真正将其融入到实际工作开发中。比如现在的 IDE 已经那么强大,各种快捷键、重构等方式足够快,然后要用 Vim,要将 Vim 打造到 IDE 那么强大,太耗时间。即使现在也有了 NeoVim 这种更现代化的 Vim,再搭配 LazyVim 可以简化配置的大杀器,确实降低了很多配置成本。对于喜欢折腾的同学,从零打造一个完全由自己掌握的 IDE 的感觉是很爽的。

笔者也喜欢折腾,但只能折腾一点,也使用 Neovim 搭建过,搭了一个基础版就放弃了,无法割舍对 IDE 已有功能的喜爱。直到发现了 VSCode 中 Vim 的插件模式,既可以保留对 VSCode 自身能力的使用,又能借助 Vim 的一些强大特性,才真正让我开始对 Vim 进行使用。

VSCode 里面的 Vim 模式插件主要有两种:

Vim

该插件模仿了 Vim 编辑器的键绑定和行为,提供了大多数 Vim 的原生功能,比如模式切换(普通模式、插入模式、可视模式等)、快捷键和命令。

优点

  • 无需外部依赖:直接在 VSCode 中实现 Vim 功能,不需要安装额外的 Vim 或 NeoVim(实验特性可以部分支持 NeoVim)
  • 易于安装和使用:从 VSCode 插件市场直接安装,配置较为简单
  • 良好的集成:与 VSCode 的功能(比如代码补全、片段、调试等)集成得较好

缺点

  • 性能问题:尽管对大多数场景已经足够,但对于一些高级功能或大文件,可能会有性能瓶颈
  • 功能限制:虽然包含了大部分 Vim 功能,但仍有一些高级特性和插件不可用或难以实现

VSCode Neovim

插件集成了 NeoVim,在非 Insert 模式时,其实是在后台运行 NeoVim 实例,并将其编辑功能带到了 VSCode 中。

优点

  • 更接近真正的 Vim/NeoVim:由于基于 NeoVim,用户可以享受到几乎所有 NeoVim 的特性,包括对 Vim 脚本的支持
  • 更好的性能:对于复杂的操作和大文件,NeoVim 插件通常表现出更好的性能
  • 更广泛的功能支持:利用 NeoVim 的强大功能,几乎可以使用所有 Vim/NeoVim 插件和配置,提供了更广泛的定制选项

缺点

  • 需要外部依赖:需要用户先安装 NeoVim,并自行配置各种特性,这对一些用户可能是额外的负担
  • 可能的集成问题:虽然功能强大,但和 VSCode 自身能力的集成可能不如纯 Vim 插件那么密切,尤其是在 UI 集成、代码补全和诊断工具方面

笔者使用 Vim 插件,在使用 Vim 之前强烈建议仔细阅读一下插件的使用文档。这里也推荐这个不错的学习资源:

一些预设值

安装 Vim 插件后,大多数面板都可以直接使用 i/j/k/l 进行移动,为了能按下按键后持续移动,还需要一些设置:

bash 复制代码
defaults write com.microsoft.VSCode ApplePressAndHoldEnabled -bool false              # For VS Code
defaults write com.microsoft.VSCodeInsiders ApplePressAndHoldEnabled -bool false      # For VS Code Insider
defaults write com.vscodium ApplePressAndHoldEnabled -bool false                      # For VS Codium
defaults write com.microsoft.VSCodeExploration ApplePressAndHoldEnabled -bool false   # For VS Codium Exploration users
defaults delete -g ApplePressAndHoldEnabled                                           # If necessary, reset global default

并且推荐将键盘的速率调快:

设置 Vim 的 Leader 键位,大多数 Vim 配置都是使用空格,开启相对行数:

json 复制代码
{
  "vim.leader": "<space>", // 设置 Leader 键为空格键
  "editor.lineNumbers": "relative", // 开启相对行号,当需要操作 x 行时,可以很方便通过相对行操作
  "vim.smartRelativeLine": true // 智能相对行号(插入模式显示绝对行号)
}

快速移动 Sneak & EasyMotion

除了 bew 这些常规移动外,通过 Sneak & EasyMotion 可以实现快速移动到某个位置,比如移动到一个名为 tool 的变量,可以直接使用 f+to(名字中随便两个相邻的字符即可)就可以快速跳过去,如果在光标和当前目标中间还有 to 字符匹配,就需要按下 ; 再次跳一下,对于相隔几行的跳转很快:

如果是隔得比较远了,就需要用到 EasyMotion,它会给所有命中的位置给一个再次选择的高亮提示,保证能精准跳过去:

json 复制代码
{
  // Vim Sneak 插件配置(快速双字符搜索)
  "vim.sneak": true, // 启用 Sneak 功能
  "vim.sneakUseIgnorecaseAndSmartcase": true, // Sneak 使用忽略大小写和智能大小写
  "vim.easymotion": true, // 启用 EasyMotion 快速移动功能
  "vim.normalModeKeyBindingsNonRecursive": [
    // 自定义移动键位映射(重新映射 f/F 为 s/S)
    {
      "after": ["s"], // 将原本的 f 键功能映射到 s 键
      "before": ["f"]
    },
    {
      "after": ["S"], // 将原本的 F 键功能映射到 S 键
      "before": ["F"]
    },
    {
      "after": ["c", "l"], // 将 s 键重新映射为 cl(change letter)
      "before": ["s"]
    },
    {
      "after": ["^", "C"], // 将 S 键重新映射为行首删除到行尾
      "before": ["S"]
    },

    // EasyMotion 快速移动键位映射
    {
      "after": ["leader", "leader", "2", "s"], // Leader+s 映射到 EasyMotion 搜索
      "before": ["leader", "s"]
    },
    {
      "after": ["leader", "leader", "2", "f"], // Leader+f 映射到 EasyMotion 查找
      "before": ["leader", "f"]
    },
    {
      "after": ["leader", "leader", "2", "t"], // Leader+t 映射到 EasyMotion 到字符前
      "before": ["leader", "t"]
    }
  ],
  "vim.visualModeKeyBindingsNonRecursive": [
    // 重新映射查找键位(与普通模式保持一致)
    {
      "after": ["s"], // f 映射到 s
      "before": ["f"]
    },
    {
      "after": ["S"], // F 映射到 S
      "before": ["F"]
    },
    // EasyMotion 快速移动(可视模式)
    {
      "after": ["leader", "leader", "2", "s"], // Leader+s 用于 EasyMotion 搜索
      "before": ["leader", "s"]
    },
    {
      "after": ["leader", "leader", "2", "f"], // Leader+f 用于 EasyMotion 查找
      "before": ["leader", "f"]
    },
    {
      "after": ["leader", "leader", "2", "t"], // Leader+t 用于 EasyMotion 到字符前
      "before": ["leader", "t"]
    }
  ],
  "vim.operatorPendingModeKeyBindingsNonRecursive": [
    // 重新映射查找键位
    {
      "after": ["z"], // 将 f 功能映射到 z(保持与普通模式一致)
      "before": ["f"]
    },
    {
      "after": ["Z"], // 将 F 功能映射到 Z(保持与普通模式一致)
      "before": ["F"]
    }
  ]
}

LSP 操作

找到了具体的地方,接下来就会进行一些代码的 LSP 查看工作,比如查看定义、引用等等。

json 复制代码
{
  "vim.normalModeKeyBindingsNonRecursive": [
    // 代码导航和信息查看操作(g 系列命令)
    {
      "before": ["g", "h"], // gh 显示定义预览悬停
      "commands": ["editor.action.showDefinitionPreviewHover"]
    },
    {
      "before": ["g", "d"], // gd 跳转到定义
      "commands": ["editor.action.revealDefinition"]
    },
    {
      "before": ["g", "p", "d"], // gpd 预览定义(不跳转)
      "commands": ["editor.action.peekDefinition"]
    },
    {
      "before": ["g", "i"], // gi 跳转到实现
      "commands": ["editor.action.goToImplementation"]
    },
    {
      "before": ["g", "p", "i"], // gpi 预览实现(不跳转)
      "commands": ["editor.action.peekImplementation"]
    },
    {
      "before": ["g", "t"], // gt 跳转到类型定义
      "commands": ["editor.action.goToTypeDefinition"]
    },
    {
      "before": ["g", "p", "t"], // gpt 预览类型定义(不跳转)
      "commands": ["editor.action.peekTypeDefinition"]
    },
    {
      "before": ["g", "r"], // gr 查找所有引用
      "commands": ["references-view.findReferences"]
    },
    {
      "before": ["g", "p", "r"], // gpr 预览引用(不跳转)
      "commands": ["editor.action.referenceSearch.trigger"]
    },
    {
      "before": ["g", "q"], // gq 快速修复
      "commands": ["editor.action.quickFix"]
    },
    {
      "before": ["g", "s"], // gs 触发智能建议
      "commands": ["editor.action.triggerSuggest"]
    },
    {
      "before": ["g", "b"], // gb 选择下一个相同项
      "commands": ["editor.action.addSelectionToNextFindMatch"]
    },
    {
      "before": ["g", "B"], // gB 选择所有相同项
      "commands": ["editor.action.selectHighlights"]
    }
  ]
}

搭配 Hover 的按键设置,即可实现 Hover 里面内容同样使用 i/j/k/l 进行移动:

json 复制代码
[
  {
    "key": "j",
    "command": "editor.action.scrollDownHover",
    "when": "editorHoverFocused"
  },
  {
    "key": "k",
    "command": "editor.action.scrollUpHover",
    "when": "editorHoverFocused"
  },
  {
    "key": "h",
    "command": "editor.action.scrollLeftHover",
    "when": "editorHoverFocused"
  },
  {
    "key": "l",
    "command": "editor.action.scrollRightHover",
    "when": "editorHoverFocused"
  }
]

也可以不配置,使用前文提到的 Caps_Lock 加上 i/j/k/l 映射为方向键也能很方便移动

代码编辑

通过 Motion 以及 vim-textobj-argumentsvim-indent-object 等能力可以方便实现对区块、参数的编辑:

对代码进行包裹也是很常见的操作,比如使用 {} 包裹,或者是删除包裹块,在进行 React 开发时,可能还会包裹一个组件,或是删除父组件,借助 vim-surround 可以快速处理:

vim-surround 只能添加括号、引号、标签等,如果想要包裹其他的,比如 if 节点,可以借助 Surround 插件:

json 复制代码
{
  "vim.visualModeKeyBindingsNonRecursive": [
    // 环绕操作
    {
      "before": ["r"], // r 用选择的字符环绕选中内容
      "commands": ["surround.with"]
    }
  ]
}

不过 Vim 和该插件插入后的编辑模式匹配不是太好。

Normal 模式下自动切换输入法

在 Insert 模式下,如果输入了中文,又没有将输入法切回英文模式,再切回 Normal 模式进行操作时,还是会展示中文输入框:

通过 Vim 的 input method 配置,搭配 im-select 可以解决输入法问题,在切换回 Normal 模式时,会自动将输入法切换为英文模式:

文中更多的只是展示一下特性,完整的 Vim 配置详见 keyboard-master 仓库。

Chrome Vim 模式

除了 IDE,浏览器几乎就是第二高频使用的软件,浏览器的常见操作大概就是:输入搜索、内容查看、内容滚动、链接点击跳转、筛选等等。这些也都可以通过 SurfingKeys 插件使用键盘来快速操作:

按下 ? 即可打开 Cheatsheet,功能非常多,记住常用的就 OK:

而且按键也是有规律的,比如 o 开头的是打开 xxx,s 开头的是搜索等等。输入一个字符,右下角也会跳出提示:

其他软件推荐

Raycast

Raycast 是 Alfred 的替代品,通过 Raycast,可以非常方便使用键盘操作各种命令,并且提供了非常多插件扩展使用,免费版也足够强大。

窗口操作

软件位置移动、窗口放大缩小,同样可以使用键盘来处理,独立的软件推荐 Rectangle

如果你已经安装了 Raycast,里面自带了窗口管理的能力,可以使用 Rectangle 快捷键预设,如果觉得不顺手,可以自定义,借助本文提到的 Karabiner-Elements 能力:

软件切换

Mac 最麻烦的就是自带的软件应用来回切换,如果某个软件最小化了,切过去后,依旧是最小化的模式,不会自动打开,需要手动点开。

可以借助 Raycast 中的 Navigation 能力优化操作,切过去时会自动打开最小化的应用:

如果觉得键盘输入不直观,还有一个软件 AltTab 也能解决这个问题,展现形式和系统自带的 UI 效果类似:

输入法切换 Input Source Pro

前面提到了针对 Vim 输入法切换的 im-select,而 Input Source Pro 则是针对所有软件的,可以针对不同的软件设置默认的输入法,比如在切换到终端软件 Warp 或者 iTerm2 时,自动切换为英文输入:

总结

无论是哪种软件的辅助,都离不开刻意练习,都需要花费不小时间成本,喜欢折腾的同学不妨试试看,不喜欢也无妨。键盘编程也不是什么必需的手段,也不是银弹,只是多一种选择。

相关推荐
耶啵奶膘2 小时前
uniapp+firstUI——上传视频组件fui-upload-video
前端·javascript·uni-app
视频砖家3 小时前
移动端Html5播放器按钮变小的问题解决方法
前端·javascript·viewport功能
lyj1689973 小时前
vue-i18n+vscode+vue 多语言使用
前端·vue.js·vscode
小白变怪兽4 小时前
一、react18+项目初始化(vite)
前端·react.js
ai小鬼头4 小时前
AIStarter如何快速部署Stable Diffusion?**新手也能轻松上手的AI绘图
前端·后端·github
墨菲安全5 小时前
NPM组件 betsson 等窃取主机敏感信息
前端·npm·node.js·软件供应链安全·主机信息窃取·npm组件投毒
GISer_Jing5 小时前
Monorepo+Pnpm+Turborepo
前端·javascript·ecmascript
天涯学馆5 小时前
前端开发也能用 WebAssembly?这些场景超实用!
前端·javascript·面试
我在北京coding6 小时前
TypeError: Cannot read properties of undefined (reading ‘queryComponents‘)
前端·javascript·vue.js
前端开发与ui设计的老司机7 小时前
UI前端与数字孪生结合实践探索:智慧物流的货物追踪与配送优化
前端·ui