Vim 模式

Zed 包含一个被称为“vim 模式”的 vim 模拟层。本文档旨在描述它的工作原理以及如何充分利用它。

理念

Zed 中的 Vim 模式主要“按预期执行”:它主要尝试完全复制 vim,但会在可用时使用 Zed 特定的功能来使事情更顺畅。

这意味着 Zed 永远不会 100% 与 Vim 兼容,但应该 100% 熟悉 Vim!我们希望我们的 Vim 模式已经能够应对您 90% 的工作流程,并且我们希望继续改进它。如果您发现 Vim 模式中无法完成的事情,但您在当前工作流程中依赖它们,请 提交问题

Zed 特定功能

Zed 建立在现代基础之上,它(除其他外)使用 tree-sitter 和语言服务器来理解您正在编辑的文件的内容,并且开箱即用地支持多个光标。

Vim 模式有几个“核心 Zed”键绑定,它们将帮助您充分利用 Zed 的特定功能集。

# Language server
g d     Go to definition
g D     Go to type definition
g cmd-d Go to implementation
c d     Rename (change definition)
g A     Go to All references to the current word

g s   Find symbol in current file
g S   Find symbol in entire project

g ]   Go to next diagnostic
g [   Go to previous diagnostic
] d   Go to next diagnostic
[ d   Go to previous diagnostic
g h   Show inline error (hover)
g .   Open the code actions menu

# Git
] c   Go to next git change
[ c   Go to previous git change

# Treesitter
] x   Select a smaller syntax node
[ x   Select a larger syntax node

# Multi cursor
g l   Add a visual selection for the next copy of the current word
g L   The same, but backwards
g >   Skip latest word selection, and add next.
g <   The same, but backwards
g a   Add a visual selection for every copy of the current word

# Pane management
g /        Open a project-wide search
g <space>  Open the current search excerpt
<ctrl-w> <space>  Open the current search excerpt in a split
<ctrl-w> g d      Go to definition in a split
<ctrl-w> g D      Go to type definition in a split

# Insert mode
i a / a a      Select the function argument the cursor is in
ctrl-x ctrl-o  Open the completion menu
ctrl-x ctrl-c  Request GitHub Copilot suggestion (if configured)
ctrl-x ctrl-a  Open the inline AI assistant (if configured)
ctrl-x ctrl-l  Open the code actions menu
ctrl-x ctrl-z  Hides all suggestions

# Ex commands
:E[xplore]    Open the project panel
:C[ollab]     Open the collaboration panel
:Ch[at]       Open the chat panel
:A[I]         Open the AI panel
:No[tif]      Open the notifications panel
:fe[edback]   Open the feedback window
:cl[ist]      Open the diagnostics window
:te[rm]       Open the terminal
:Ext[ensions] Open the extensions window

Vim 模式使用 Zed 来定义“括号”(用于 % 键)和“单词”(用于 we 等动作)等概念。这确实导致了一些差异,但它们大多是积极的。例如,% 在 Rust 等语言中将 | 视为括号;而 w 在 Javascript 等语言中将 $ 视为单词字符。

Vim 模式使用 Zed 的多光标支持模拟可视块模式。这再次导致了一些差异,但功能更强大。

Vim 的宏支持(q@)是使用 Zed 的操作实现的。这让我们能够支持录制和重放自动完成的代码等。与 Vim 不同,Zed 不会重复使用 yank 寄存器来录制宏,它们是两个独立的命名空间。

最后,Vim 模式的搜索和替换功能是由 Zed 支持的。这意味着模式语法略有不同,有关详细信息,请参阅 Regex 差异 部分。

自定义键绑定

你可以使用 :keymap 编辑你的个人键绑定。对于 vim 特定的快捷方式,你可能会发现以下模板是一个很好的起点。

注意:我们在 Zed 版本 0.145.0 中进行了一些重大更改。对于较旧版本,请参阅 此文档的先前版本

[
  {
    "context": "VimControl && !menu",
    "bindings": {
      // put key-bindings here if you want them to work in normal & visual mode
    }
  },
  {
    "context": "vim_mode == insert",
    "bindings": {
      // "j k": "vim::NormalBefore" // remap jk in insert mode to escape.
    }
  },
  {
    "context": "EmptyPane || SharedScreen",
    "bindings": {
      // put key-bindings here (in addition to above) if you want them to
      // work when no editor exists
      // "space f": "file_finder::Toggle"
    }
  }
]

如果你想模拟 vim 的 mapnmap 等)命令,可以在正确的上下文中绑定到 workspace::SendKeystrokes 操作。

你可以 在此处 查看 vim 模式下默认启用的绑定。

上下文

只有当 "context" 与你屏幕上的位置匹配时,才会评估 Zed 的键盘绑定。位置是嵌套的,因此当你进行编辑时,你处于 "Workspace" 位置的顶部,其中包含一个 "Pane",该 "Pane" 包含一个 "Editor"。一次只匹配一个级别的上下文。因此可以组合 Editor && vim_mode == normal,但 Workspace && vim_mode == normal 永远不会匹配,因为我们在 Editor 级别设置了 vim 上下文。

Vim 模式向 Editor 添加了几个上下文

  • vim_mode 类似于当前模式,但并不相同。它从 normalvisualinsertreplace 之一开始(取决于你的模式)。如果你正在键入序列的中间,vim_mode 将是 waiting(如果它正在等待一个任意键,例如在键入 ft 之后),或者 operator(如果它正在等待另一个绑定触发,例如在键入 cd 之后)。
  • vim_operator 设置为 none,除非 vim_mode == operator,在这种情况下,它设置为当前操作符的默认键绑定(例如在键入 d 之后,vim_operator == d)。
  • "VimControl" 表示 vim 键绑定应该起作用。它当前是 vim_mode == normal || vim_mode == visual || vim_mode == operator 的别名,但定义可能会随着时间的推移而改变。

恢复一些正常感

如果你在 Linux 或 Windows 上使用 Vim 模式,你可能会发现它覆盖了你不可或缺的键绑定。你可以通过将它们复制到你的键映射中来恢复它们的默认值

{
  "context": "Editor && !menu",
  "bindings": {
    "ctrl-c": "editor::Copy",          // vim default: return to normal mode
    "ctrl-x": "editor::Cut",           // vim default: increment
    "ctrl-v": "editor::Paste",         // vim default: visual block mode
    "ctrl-y": "editor::Undo",          // vim default: line up
    "ctrl-f": "buffer_search::Deploy", // vim default: page down
    "ctrl-o": "workspace::Open",       // vim default: go back
    "ctrl-a": "editor::SelectAll",     // vim default: increment
  }
},

命令面板

Vim 模式允许你使用 : 启用 Zed 的命令面板。这意味着你可以使用 vim 的命令面板运行 Zed 支持的任何操作。

此外,vim 模式包含许多流行 vim 命令的别名,以确保肌肉记忆起作用。例如,:w<enter> 将保存文件。

我们(还没有)模拟 vim 命令行的全部功能,特别是我们特殊处理特定模式,而不是使用 vim 的范围选择语法,并且我们还不支持命令参数。请在 GitHub 上联系我们,因为你发现命令面板中缺少的东西。

如上所述,需要注意的一件事是,在 :%s/a/b 中,正则表达式引擎与 vim 的略有不同。

当前支持的 vim 特定命令

# window management
:w[rite][!], :wq[!], :q[uit][!], :wa[ll][!], :wqa[ll][!], :qa[ll][!], :[e]x[it][!], :up[date]
    to save/close tab(s) and pane(s) (no filename is supported yet)
:cq
    to quit completely.
:vs[plit], :sp[lit]
    to split vertically/horizontally (no filename is supported yet)
:new, :vne[w]
    to create a new file in a new pane above or to the left
:tabedit, :tabnew
    to create a new file in a new tab.
:tabn[ext], :tabp[rev]
    to go to previous/next tabs
:tabc[lose]
    to close the current tab

# navigating diagnostics
:cn[ext], :cp[rev], :ln[ext], :lp[rev]
    to go to the next/prev diagnostics
:cc, :ll
    to open the errors page

# jump to position
:<number>
    to jump to a line number
:$
    to jump to the end of the file
:/foo and :?foo
    to jump to next/prev line matching foo

# replacement (/g is always assumed and Zed uses different regex syntax to vim)
:%s/foo/bar/
  to replace instances of foo with bar
:X,Ys/foo/bar/
    to limit replacement between line X and Y
    other ranges are not yet implemented

# editing
:j[oin]
    to join the current line (no range is yet supported)
:d[elete][l][p]
    to delete the current line (no range is yet supported)
:s[ort] [i]
    to sort the current selection (with i, case-insensitively)

由于任何 Zed 命令都可用,你可能会发现记住运行正确命令的助记符很有帮助。例如

:diff   Toggle Hunk [Diff]
:diffs  Toggle all Hunk [Diffs]
:revert Revert Selected Hunks
:cpp    [C]o[p]y [P]ath to file
:crp    [C]opy [r]elative [P]ath
:reveal [Reveal] in finder
:zlog   Open [Z]ed Log

设置

Vim 模式默认情况下未启用。要启用 Vim 模式,你需要在设置文件中添加以下配置

{
  "vim_mode": true
}

或者,你可以通过从命令面板中运行 切换 vim 模式 命令来启用 Vim 模式。

一些 vim 设置可用于修改默认 vim 行为

{
  "vim": {
    // "always": use system clipboard when no register is specified
    // "never": don't use system clipboard unless "+ or "* is specified
    // "on_yank": use system clipboard for yank operations when no register is specified
    "use_system_clipboard": "always",
    // Lets `f` and `t` motions extend across multiple lines
    "use_multiline_find": true
  }
}

如果你使用 vim 模式,还有一些 Zed 设置你可能也会喜欢

{
  // disable cursor blink
  "cursor_blink": false,
  // use relative line numbers
  "relative_line_numbers": true,
  // hide the scroll bar
  "scrollbar": { "show": "never" },
  // allow cursor to reach edges of screen
  "vertical_scroll_margin": 0,
  "gutter": {
    // disable line numbers completely:
    "line_numbers": false
  },
  "command_aliases": {
    "W": "w",
    "Wq": "wq",
    "Q": "q"
  }
}

如果你想在编辑器和停靠区(终端、项目面板、AI 助手,...)之间导航,就像在拆分之间导航一样,可以使用以下键绑定

{
  "context": "Dock",
  "bindings": {
    "ctrl-w h": ["workspace::ActivatePaneInDirection", "Left"],
    "ctrl-w l": ["workspace::ActivatePaneInDirection", "Right"],
    "ctrl-w k": ["workspace::ActivatePaneInDirection", "Up"],
    "ctrl-w j": ["workspace::ActivatePaneInDirection", "Down"]
    // ... or other keybindings
  }
}

子词移动默认情况下未启用。要启用它,请将这些绑定添加到你的键映射中。

  {
    "context": "VimControl && !menu",
    "bindings": {
      "w": "vim::NextSubwordStart",
      "b": "vim::PreviousSubwordStart",
      "e": "vim::NextSubwordEnd",
      "g e": "vim::PreviousSubwordEnd"
    }
  },

在可视模式下包围选择内容也没有默认启用(shift-s 通常的行为像 c)。要启用它,请将以下内容添加到你的键映射中。

  {
    "context": "vim_mode == visual",
    "bindings": {
      "shift-s": [
        "vim::PushOperator",
        {
          "AddSurrounds": {}
        }
      ]
    }
  }

支持的插件

Zed 对一些 Vim 插件有新生的支持

  • vim-surroundyscsds 可以工作。不过你无法添加新的 HTML 标记。
  • vim-commentary,在可视模式下为 gc,在正常模式下为 gcc。不过你无法对任意对象进行操作。
  • netrw,大多数键绑定在项目面板中都受支持。
  • vim-spider/CamelCaseMotion 你可以使用如上所述的子词移动。

正则表达式差异

Zed 使用与 Vim 不同的正则表达式引擎。这意味着你必须对某些内容使用不同的语法。

值得注意的是

  • Vim 使用 \(\) 来表示捕获组,在 Zed 中它们是 ()
  • 另一方面,() 表示字面上的括号,但在 Zed 中它们必须转义为 \(\)
  • 替换时,Vim 使用 \0 来表示整个匹配,在 Zed 中这是 $0,对于编号捕获组也是如此,\1 -> $1
  • Vim 使用 /g 来表示“一行上的所有匹配”,在 Zed 中这是隐含的
  • Vim 使用 /i 来表示“不区分大小写”,在 Zed 中,你可以在模式的开头使用 (?i) 或使用 cmd-option-c 切换大小写敏感性。

为了帮助过渡,当运行 :%s// 时,命令面板将为你修复括号并替换组。因此,%s:/\(a\)(b)/\1/ 将被转换为搜索 “(a)(b)”和替换为 “$1”。

有关 Zed 正则引擎支持的完整语法,请参阅 regex crate 文档