04-打开文件
第 4 章 打开文件
在上一章中,作为学习命令模式的副产品,我们看到了如何用老式 Vim 的方式打开文件,即使用 :edit
命令。另一种老派的替代方法是直接从终端 shell 命令行打开它们,使用 nvim 文件名
。
译者注:
:edit
或其缩写:e
是 Vim/Neovim 中用于打开文件进行编辑的 Ex 命令。例如:e path/to/your/file.txt
。
这两种方法偶尔会派上用场,但 LazyVim 预先配置了更现代的文件导航和打开方式。
4.1. 文件选择器简介
LazyVim 自带 snacks.nvim
插件,该插件包含了一系列现代化的、提升生活质量的改进。它由 LazyVim 的创建者 Folke Lemaitre 维护,因此我们有理由相信它们能很好地集成LazyVim。它实际上是一系列执行各种任务的小型插件的集合。
译者注: “提升生活质量” (Quality-of-life) 是软件开发中的常用语,指那些不一定增加核心功能,但能让用户体验更流畅、更便捷、更舒适的改进。国内软件工程很少用这个词,所以就原封不动的翻译了。
我们将讨论的第一个 snacks 集成是一个高性能的“选择器”(picker) 工具,用于从列表中选择项目。该选择器界面包含预览和模糊搜索功能。如果你用过许多现代编辑器(甚至 Github 或 Slack)中的命令菜单,你可能知道我在说什么。选择器本身不关心你选择的是什么,它被用于 LazyVim 内置或作为第三方插件的许多不同任务,包括打开文件、选择已打开的缓冲区、项目范围内的搜索等等。
译者注:
- 选择器 (Picker): 一种 UI 组件,通常是一个弹出窗口,允许用户通过输入(通常是模糊搜索)快速过滤并选择列表中的项目(如文件、命令、缓冲区等)。
- 模糊搜索 (Fuzzy Search/Finding): 一种搜索技术,允许匹配不完全精确的字符串。例如,输入 “fzf” 可能匹配 “fuzzy-finder” 或 “foo_zeta_file”,因为它会查找按顺序出现但不必连续的字符。
你将执行的最常见的选择器任务是使用模糊搜索打开文件。我每天使用这个命令几十次,甚至可能上百次,所以幸好它有一个非常容易按到的快捷键。
文件选择器最好在一个包含很多文件的代码仓库中工作时进行演示。所以用 Space q q
关闭 Neovim,并在你的终端中使用 cd
命令切换到你最近一直在处理的一个项目的目录(如果你手头没有,克隆你最喜欢的开源项目并使用它)。然后输入 nvim
再次打开 Neovim。
译者注:
<Space> q q
是 LazyVim 中退出 Neovim 的快捷键序列(先按空格,再按两次 q)。
我让你退回到终端是因为这样更容易理解,但也可以使用 :cd
命令从 LazyVim 内部更改目录。输入 :cd the/path/to/the/directory
并按 Enter
,记住你可以使用 Tab
键来自动补全路径。现在如果你使用 :e
打开文件,它们将相对于你指定的目录。如果你使用文件选择器,它们可能相对于该工作目录或包含当前文件的项目,稍后讨论。使用 :pwd
查看当前目录是什么。
译者注:
:cd <目录路径>
: 更改 Neovim 的当前工作目录 (Change Directory)。:pwd
: 显示 Neovim 的当前工作目录 (Print Working Directory)。
好的,所以你现在位于一个大型项目的根目录,并且想要打开任意一个文件。只需按两次 Space
键(即 Space Space
)即可弹出“当前项目中的文件”(Files In Current Project) 选择器。正如我提到的,这是你整个键盘上最容易输入的快捷键。大多数键盘上的空格键都很大,而且你是用你最强壮的手指——拇指来按它。像往常一样,只按一次 Space
会弹出 Space 模式菜单,你可以看到再按一次 Space
将为你呈现“查找文件 (根目录)”(Find Files (Root Dir))。
对于包含本书当前状态的项目,选择器看起来像这样:
图 16. Snacks 选择器
选择器分为三个主要区域:左上角的输入区域(在此标记为“Files”),左下角的结果列表,以及右侧当前选定文件的预览。
输入区域当前处于激活状态并且是插入模式,所以你可以直接开始输入任何你想打开的文件的名称。这是一个“模糊搜索”(由 Sublime Text 推广的概念),这意味着你可以跳过字母,从而节省哦-那么-宝贵的毫秒。例如,如果我输入 ch3
,我的列表会被过滤为以下文件:
图 17. 选择第 3 章
只有路径中按顺序包含这三个字符(中间可能夹杂其他字符)的文件是可见的。选择器很贴心地在结果中高亮了这三个字母,这样你就能轻易看出它为什么匹配。
还要注意,默认情况下,匹配是大小写不敏感的。我输入了小写字母 c
,但它匹配了文件名中的大写 C
。这通常足以将搜索结果缩小到你需要的范围。然而,如果你在搜索中确实使用了任何大写字母,它会切换到大小写敏感模式(这有时被称为“智能大小写匹配”(smart case))。
这意味着 Ch
会匹配所有 Chapters
,但 cH
不会匹配任何东西。更有趣的是,chF
也同样不会匹配任何东西,因为大写 F
的存在使得整个搜索变为大小写敏感,而所有章节名称都是以大写 C
开头的,所以小写 c
无法匹配它们。
有时你会开始输入一个词,然后意识到你需要匹配路径中更早的内容来区分它。例如,我在 Fablehenge 的这些源文件中开始输入 outline
:
图 18. 选择器中的 Outline
“Outline”在这个应用中是个常用词。有 243 个匹配文件,我意识到我或许应该在前面输入 comp
来将其范围缩小到仅 component
目录下的文件。我可以切换到普通模式并编辑行首,但更快的方法是直接输入 <space>comp
。选择器会将空格解释为“再次过滤这些行,从头开始模糊匹配这个新词”。这里我们可以看到只有 comp…outline
文件被匹配了:
图 19. 将选择器范围缩小到 “comp”
这张图片可能令人惊讶;最可能的匹配显然是列表顶部的那个选中的。其他 35 个匹配行都按从左到右的顺序包含了单词“outline”的所有字母和单词“comp”的所有字母。然而,由于模糊匹配算法,这两者实际上可以重叠!例如,在 outline1.png
条目上,匹配“comp”的 c
在路径的 src
部分,位于单词 outline
之前,o
在其中,而 m
和 p
都在单词 outline
之后。选择器并不在乎,尽管它会将匹配字母更靠近的匹配项排得更重要,所以它们会显示在结果的顶部。
你可以使用上下箭头键在搜索结果中选择不同的文件,其预览将显示在右侧窗口中。一旦找到你想打开的文件,按 Enter
键在当前活动的 Neovim 窗口中打开它。
你甚至可以使用一种 Seek 模式,就像我们在第 3 章讨论的那样,尽管它在选择器中的工作方式略有不同。在选择器的输入区域按 Alt-s
键。你会看到选择器中的每一行旁边都出现一个标签:
图 20. Seek 标签
这些字符是选择器中每一行的标签。只需在键盘上按下显示的字母之一,与该字母关联的标签所在的行就会被选中。然后按 Enter
实际打开文件(或者,如果不是文件选择器,则执行该选择器的默认操作)。
如果你想从选择器中打开多个文件,不要按 Enter
,而是按 Tab
来选择文件。导航到其他行并按 Tab
也选择它们。按 Enter
确认你的选择。我们稍后会讨论你可以对一组选定文件执行的其他操作。
如果你需要滚动结果窗口以查看列表中更靠下的内容,请使用 Control-d
和 Control-u
键。如果你想滚动预览窗口,请改用 Control-f
和 Control-b
。
最后,如果你在选择器窗口中,并且最终决定不想打开任何文件(或者你已经从预览中获得了所需的信息),请按 Escape
两次。为什么是两次?第一次按 escape 会将你置于普通模式,这样你就可以使用所有常用的普通模式命令来编辑你的过滤器。第二次按 escape 才会关闭选择器。
4.2. “Root”(根目录)和 “Cwd”(当前工作目录)的区别
<Space><Space>
命令映射到“查找文件 (根目录)”。另外两种打开文件选择器的方法是使用 <Space>f
打开“文件/查找”菜单,然后跟上 f
或 F
。
<Space>ff
与 <Space><Space>
相同。它打开“查找文件 (根目录)”,只是到达那里的另一种更长的方式。我假设它同时存在于两个地方,这样用户可以选择将其他操作映射到超级易用的 <Space><Space>
,同时仍然能够通过 <Space>ff
访问选择器功能。
<Space>fF
,其中第二个 F
是大写的,略有不同;它映射到一个名为“查找文件 (cwd)”的操作。如果你在你的项目中运行它,你很可能会发现它看起来与“查找文件 (根目录)”做的完全一样(取决于你的项目设置),所以两个独立快捷键的目的可能会令人困惑。
4.2.1. 当前工作目录 (Current Working Directory)
“Cwd” 代表“当前工作目录”(Current Working Directory),默认情况下,它指的是你输入 nvim
打开编辑器时终端所在的目录。你可以通过用 :
进入命令模式,然后输入 cd 路径/目录
来更改整个编辑器的 cwd
(记住,所有命令后面都要跟一个回车符,所以之后按 Enter
或 Return
)。现在如果你使用 <Space>fF
,文件列表将相对于你切换到的新目录显示。
如果你不确定你在哪个目录,可以使用 :pwd
(“打印工作目录”print working directory 的缩写)命令让它在一个小通知窗口中弹出。cd
和 pwd
与 bash
、zsh
和许多其他 shell 用来更改和打印工作目录的命令相同,所以你已经很熟悉它们了。
我们还没有讨论过分割编辑器或打开新标签页:实际上我们可以为不同的窗口设置不同的工作目录。仅更改当前窗口目录的命令是 :lcd
,即“本地更改目录”(local change directory) 的缩写。这是能同时处理多个项目的强大方式(例如,如果你是处理后端和前端项目的全栈开发者)。然而,LazyVim 的“根”目录概念可以半自动化这个过程。
译者注:
- cwd (Current Working Directory): Neovim 进程当前操作的基准目录。影响相对路径的解析,如
:e file.txt
。:cd <path>
: 改变整个 Neovim 实例的 cwd。:lcd <path>
: 仅改变当前窗口的 cwd。这对于在不同窗口处理不同项目很有用。:pwd
: 显示当前的 cwd。
4.2.2. 根目录 (Root Directory)
根目录不是一个 Vim 概念,而是语言服务器协议 (LSP) 的概念。LSP 是 VS Code 如此迅速流行的原因;其思想是编辑器可以调用运行在你计算机上的外部服务来获取关于代码库的有用信息。LSP 支持许多有用的功能,如转到定义和引用、高亮代码中的错误以及显示变量或类的文档。它甚至可以帮助格式化和语法高亮。
译者注: LSP (Language Server Protocol): 一种标准化协议,允许编辑器/IDE(如 Neovim, VS Code)与提供语言智能(如自动完成、定义跳转、错误检查)的独立“语言服务器”进程通信。
根目录是 LSP 推断出的当前打开文件所属的“主”目录。LSP 如何做到这一点取决于语言(和语言服务器)。例如,在 Javascript 或 Typescript 项目中,它可能会搜索父目录是否存在 package.json
或 tsconfig.json
文件来检测根目录。而在 Python 项目中,它可能转而寻找像 pyproject.toml
或 poetry.lock
这样的文件。Rust 项目则使用包含 Cargo.toml
的目录。或者,一些 LSP 可能仅仅使用 .git
文件夹的存在作为项目工作区的“根”。
这个根目录“通常与你的 cwd
相同”的唯一原因是,这通常是你处理项目时想要工作的文件夹,所以它是你在打开 Neovim 之前 cd
进入的那个文件夹。
这种自动根目录功能在你处理多个项目时可能非常有用。你可以不用像上一节讨论的那样使用 lcd
,只需使用 :e
或我们接下来将讨论的文件查找扩展之一在不同的项目中打开一个文件。然后,如果你使用 <Space><Space>
或 <Space>ff
调用“查找文件 (根目录)”命令,它将在你刚刚打开的文件所在的同一根目录中查找其他文件。
然而,它有时可能会令人困惑,特别是在你使用 monorepo(单一代码仓库)或者你的根目录位于意想不到的地方时。例如,我有一个相当普通的 Svelte 项目,其中包含一个 package.json
文件。这个项目使用 Cypress 进行测试,而 Cypress 文件夹包含一个 tsconfig.json
文件,这导致 Typescript 语言服务器将其解释为一个单独的根。所以如果我正在处理一个 cypress 测试文件并按下 <Space><Space>
,根目录就被认为是 Cypress 文件夹,我只能打开其他的 Cypress 测试。但通常我想做的是打开主文件夹中的源文件,看看为什么测试失败。在这种情况下,我必须按 <Escape>
退出选择器,然后按 <Space>fF
以当前工作目录模式打开选择器。
译者注: Monorepo 是一种软件开发策略,其中许多项目的代码存储在同一个版本控制仓库中。这可能导致 LSP 根目录检测变得复杂。
snacks 选择器的灵感来源于命令行工具 fzf,意为 “fuzzy find”(模糊查找)。这个工具允许你从 shell 快速访问文件和打开目录,我强烈推荐它,用这个就可以了。
4.3. Snacks Explorer 插件
Snacks.nvim 还有一个 “explorer”,一个左侧边栏的文件浏览器体验,对于许多现代 IDE 和编辑器的用户来说应该很熟悉。虽然,像那些环境中的许多一样,explorer 可以用鼠标操作,但它为键盘交互进行了优化,一旦你学会了“Explorer 模式”,使用起来会更快。
我想在这里坦诚布公的说:我个人不使用 Explorer。我发现我们刚刚讨论的文件选择器是打开文件的最快方式,而当需要操作文件系统时,我更喜欢使用 mini.files
,我们将在本章后面讨论它。我更喜欢 mini.files
的主要原因是它使用与 Vim 普通模式相同的快捷键,而不是需要我记住一个自定义的 “explorer 模式”。模式是好东西,但拥有繁杂多余的模式就不是了!
然而,我怀疑许多读者会更喜欢 explorer 提供的熟悉的树状视图体验,并且由于这个插件默认随 LazyVim 一起提供,我想确保它在本书中得到平等的介绍。
让我们从使用 <Space>-e
快捷键打开一个 explorer 开始,助记符是“e 代表 Explore”。如果你弹出 Space 模式菜单,你会看到,与选择器一样,有两种打开 explorer 的方式:<Space>-e
用于 Explore Snacks (根目录)
和 <Space>-E
用于 Explore Snacks (cwd)
。
“根目录”和“cwd”的含义与我们在上一节讨论的相同,你会注意到小写和大写字母之间的一致关系:<Space>ff
和 <Space>e
都打开根目录,而 <Space>fF
和 <Space>E
都打开当前工作目录。
要隐藏 explorer 窗口,只需在它可见时再次按 <Space>e
,或者在它获得焦点时按 q
或 Escape
。
当 explorer 打开时,它会显示相关目录中的所有文件和文件夹,所有文件夹都折叠起来,除了包含当前活动文件的那个(如果有的话)。例如,在编辑这个文件时,我的 explorer 看起来如下:
图 21. Explorer
光标位于我当前正在编辑的文件上。我可以使用无处不在的 j
和 k
键上下移动光标。
文件夹被收集到视图的顶部。如果你将光标移动到这些文件夹之一,可以按 Enter
键查看文件夹内的文件。如果你将其移动到一个文件上,也可以用 Enter
在当前的 Vim 窗口中打开该文件。
你还可以使用 Tab
选择多个文件进行操作,类似于选择器窗口(事实上,explorer 只是一个伪装起来的花哨的选择器窗口)。
你也可以通过鼠标双击来展开和折叠文件夹以及打开文件,但我猜一旦你学会了正确的键盘导航,你就不想这么做了。
说到键盘导航,是的,如果有很多文件需要导航,使用 j
和 k
上下移动可能会非常慢。我们在第 3 章讨论的所有命令都可以用来更快地移动。例如,10j
只需三次按键就能将光标向下移动 10 行,而按 10 次 j
则需要更多次,Control-d
或 Control-u
可以用来向下或向上滚动树状视图。
当 explorer 获得焦点时,使用 i
进入插入模式以搜索特定文件。由于这本质上是一个选择器,可以使用 Alt-s
来 Seek 到选择器视图中的任何行。你也可以使用普通模式的 s
命令来 seek 任何窗口中的文本,包括 explorer。
explorer 会将根目录或 cwd 显示为最顶层目录。如果你需要沿树状结构“向上”导航到更高级别的目录,你需要使用 Backspace
键。
Backspace 在 Vim 中通常编码为 <BS>
,所以如果你看到一个快捷键或说明告诉你 <BS>
做某事,他们不是在bullshit!它只是指 Backspace 键。
除了导航和打开文件,你甚至可以使用 explorer 对文件系统进行更改。例如,要删除一个文件,你可以将光标移到该文件上并按 d
键。你会看到一个弹出窗口提示你是否确定。按 y
然后按 Enter
来确认它:
图 22. Explorer 中的删除确认
要添加文件或文件夹/目录,请使用 a
键并输入新名称。使用末尾的斜杠 (/
) 来表示文件夹。
r
键可用于重命名光标下的文件或文件夹。
要复制或移动文件,你可以使用 explorer 的伪剪贴板。我说“伪”是因为你不能用它来复制文件然后粘贴到例如 MacOS Finder 或 Windows Explorer 中;只能粘贴到 explorer 中的其他位置。
如果你想复制文件,请使用 y
。y
的助记符是 yank
(提取),实际上这与你在普通编辑器中用来复制文本的键相同。要完成复制,你需要导航到目标文件夹并使用 p
键(你可能记得它表示“put”或“paste”)。
译者注:
y
(yank) 和p
(put) 是 Vim 中复制和粘贴的标准动词。
使用 m
快捷键将文件移动到新位置或重命名。
explorer 还能做大量其他很酷的事情。你可以随时在 explorer 窗口获得焦点时使用 ?
键来获取概览。
4.4. Mini.files 替代方案
正如我提到的,我实际上不使用 explorer 进行文件导航。我觉得它有点“外来的,不像 Vim”。对我来说,它是一个完全独立的体验,只是恰好嵌入在一个 Neovim 窗口中。话虽如此,我也不喜欢 VS Code 以及模仿它/被它模仿的编辑器中的树状视图侧边栏体验,所以可能树状视图就是不适合我。
这些只是我的观点,文本编辑器的黄金法则之一是“所有观点都是有效的”(否则就会爆发战争)。大量 Neovim 用户喜欢 explorer,如果它符合你的心智模型,你就使用它。
话虽如此,我的这些观点显然并不孤单,因为 LazyVim 通过一个名为 mini.files 的插件,选择性地提供了不同的文件管理体验。它默认是禁用的。
译者注: Mini.files 提供了一种叫做“米勒列”(Miller Columns) 的文件浏览界面,常见于 MacOS Finder。它并排显示目录层级,当你选择一个目录时,其内容会显示在右侧的新列中。这种布局对于深层嵌套的目录结构导航可能更直观。
Mini.files 是一套 Neovim 包(称为 mini.nvim)的一部分。这些插件彼此独立,提供了许多常见功能,在许多情况下,这些功能本应随 Neovim 一起提供。偶尔,mini.nvim 插件会劣于它们模仿的其他插件。Mini.files 不是 LazyVim 自带的唯一 mini.nvim 插件,我们稍后会接触其他的。
mini.files 文件管理器有点像 Neovim 原生体验版的列式视图(columnar view),这种视图在 MacOs finder 和其他一些文件管理器中很流行。我喜欢它的主要原因是编辑目录列表就像编辑普通的文本缓冲区一样。我不必记住 a
在普通模式下表示“之后”(after),但在 Explorer 模式下表示“添加文件/文件夹”(add file/folder)。相反,在 mini.files 中,我使用 o
键来“在当前行下方创建新行”,然后在 Neovim 插入模式下输入新文件名。稍后,我告诉 mini.files 同步我的更改,它将为新行创建文件。
为了使用 mini.files,你必须将其作为 Lazy Extra 启用。我们将在下一章更详细地介绍这一点,但现在,这些步骤应该足够了:
- 输入
:LazyExtras<Enter>
- 将光标移动到包含 mini.files 的那一行(Seek 模式最快)
- 按
x
安装该 eXtra - 稍等片刻让插件安装
- 重启 Neovim
4.4.1. 使用 Mini.files
安装后,你可以使用 <Space>fm
和 <Space>fM
显示 mini.files 视图。默认情况下,这些不完全等同于我们在选择器和 explorer 中看到的 cwd/root
结构。相反,它们在 <Space>f
菜单中列出如下:
列表 11. Mini.files 快捷键
m -> 打开 mini.files (当前文件所在目录)
M -> 打开 mini.files (cwd)
默认的 mini.files 配置没有“在根目录中打开”的选项。我喜欢能够打开当前打开文件所在目录的功能,但我不喜欢失去打开当前项目根目录的功能。我在第 5 章展示了如何解决这个问题。
mini.files 菜单不是显示为侧边栏,而是作为并排的窗口列(称为米勒列)出现。例如,当我打开 mini.files 到本书的当前工作目录时,情况如下:
图 23. Mini.files
左侧面板显示当前工作目录,中间列显示 book
目录的内容,我的光标当前在第 4 章上。右列显示该第 4 章目录的预览,其中只包含一个文件。
与 mini.files 交互与和标准的 vim 窗口交互非常相似。你可以使用 j
和 k
键上下移动光标。如果这使你的光标位于文件夹上,该文件夹的内容将立即显示在右侧;如果它位于文件上,你将看到该文件的预览。
如果你想移动“进入”一个文件夹以与其内容交互,只需按 l
键向“右”移动。
类似地,按 h
将“退出”当前文件夹。如果光标在最左边的列,向左移动将打开一个新的最左边的列,所以如果需要,你可以一直导航到文件系统的根目录。
要在当前活动的 Neovim 窗口中打开一个文件,请再次在该文件上按 l
。这里的行为可能有点令人惊讶;文件将在 mini.files 视图下方打开,但它不会隐藏文件菜单。这允许你在关闭导航器(可以用 q
键完成)之前打开多个文件。
mini.files 相较于 explorer 的美妙之处在于,这些小窗口就像普通编辑器一样运作,你已经习惯的所有导航功能都可用。例如,创建文件或文件夹是用 o
命令完成的,这与在普通编辑器中打开新行的命令相同。
我们还没怎么讲编辑(我和你一样惊讶),但这里有一个快速概述:
- 要重命名文件或文件夹,导航到包含它的行,然后进入插入模式更改或添加文本。
- 删除文件或文件夹使用
dd
命令,这是在普通 Neovim 窗口中删除整行文本的快捷键。 - 使用
yy
复制文件或文件夹,这是复制(“yank”)一行文本的命令。 - 使用
p
放置/粘贴已删除或 yanked 的文件。
我们将在第 6 章讨论这些命令以及更多内容。要点是,你将来学习的几乎任何导航或编辑命令都将适用于 mini.files。
保存文件系统更改
你使用这些快捷键所做的任何修改,在你输入 =
键之前,实际上都不会保存在文件系统上。=
是一个(罕见的)mini.files 特定快捷键。我把它理解为意为“使文件系统等于我输入的内容”。这会弹出一个小窗口,告诉你 mini.files 想代表你执行哪些操作,例如删除、移动、重命名或复制文件。你可以用 y
或 n
(当然是 yes 或 no)确认或拒绝更改。
我鼓励你同时尝试 Snacks explorer 和 mini.files,直到你能决定更喜欢哪一个。最终,你会得出以下结论之一:
- 你更喜欢 explorer,不需要 mini.files。在这种情况下,重新访问 LazyExtras 模式并用
x
键禁用 mini.files。 - 你将 explorer 用于某些交互(可能是我们尚未涵盖的内容,例如导航 git、缓冲区或符号),而将 mini.files 用于其他交互。在这种情况下,你可能对 mini.files extra 的默认 LazyVim 配置感到满意。
- 你是我这种怪人,根本不想使用 explorer,只喜欢 mini.files。禁用插件将在下一章讨论。
4.5. 总结
在本章中,我们学习了在 LazyVim 中打开文件和与文件系统交互的三种不同方式:Snacks 选择器、explorer 和 mini.files。每种方式都提供了不同的打开和管理文件的机制,你会发现其中一些比其他的更适合你。
作为研究这些文件系统工具的副产品,我们对配置插件和安装 LazyVim extras 有了一个小小的预览。我们将在下一章更详细地讨论这一点。