Skip to content

06|项目搜索与 Quickfix

项目里只有一个文件时,/ 通常就够用了。等文件多起来,还得再认识两个面向整个项目的工具:

  • Space /:在整个项目中搜索;
  • Quickfix:把多条搜索结果保留在底部,方便逐项跳转。

这一章最后,我们会直接用 Quicker 编辑 Quickfix 结果,再把修改写回多个文件。动到的范围比较大,所以开始前先留一份 Git 基线。

1. 开始前的项目检查点

src/pocket_tasks/model.py 应为:

python
from dataclasses import dataclass
from datetime import datetime

@dataclass(slots=True)
class Task:
    title: str
    created_at: datetime
    completed: bool = False
确认 Quickfix 练习前的 model.py 基线
确认 Quickfix 练习前的 model.py 基线

README 里应该已经有 GoalsQuick start。如果内容还是对不上,先照上一章结尾的检查点补齐。

先提交一份便于恢复的基线

右侧内置终端要到第 10 章才会介绍,这一节先用外部终端。

  1. 在 Neovim 中按 F1,输入 wall,选中同名命令后连按两次 Enterwall 会保存所有已修改文件。
  2. F1,输入 wqa,连按两次 Enter 退回终端。
  3. pocket-tasks 目录中依次运行:
bash
git status --short
git add .
git commit -m "checkpoint before quickfix"

如果因为没有 Git 身份而提交失败,可以只给当前练习仓库设一个临时身份,然后重新提交:

bash
git config user.name "Nvim Student"
git config user.email "nvim@example.invalid"
git commit -m "checkpoint before quickfix"

这两项只会写进当前仓库的 .git/config,不会改到其他项目,更不会动全局 Git 身份。

如果第三条命令提示没有可提交的内容,说明手头已经有一份可用的 Git 基线,可以继续。然后重新打开项目:

bash
nvim .

如果后面的批量修改出了问题,先退出 Neovim,再到项目根目录运行 git diff,看看究竟改了哪些地方。只有确定要放弃这一章的全部未提交改动时,才能用 git restore .。这个命令会丢掉当前项目里所有还没提交的修改,运行前一定要再次确认自己所在的目录。

2. 工作流一:用 Space / 准确定位内容

这一轮只记四件事:

按键在项目搜索 Picker 中的效果
Space /打开项目全文搜索
Ctrl+n / Ctrl+p选中下一条 / 上一条结果
Enter打开当前结果并跳到命中位置
Esc关闭 Picker,不打开任何结果

使用搜索定位 README

  1. 确认在普通模式,按 Space /

  2. Picker 出现后,直接输入 Quick start。候选会随着输入不断缩小,不用先按回车。

搜索 Quick start 并预览 README
搜索 Quick start 并预览 README
  1. 若有多条结果,用 Ctrl+nCtrl+p 上下选择。
  2. 选中 README 里的 ## Quick start,按 Enter

Picker 会随即关闭,README.md 会在主编辑窗口里打开,光标落在匹配位置附近。

打开 README 中的 Quick start
打开 README 中的 Quick start

现在按 G 到文件末尾,按 o 新建一行,输入下面这段:

markdown

## Data model

A task stores:

- `title`
- `created_at`
- `completed`

输完以后按 Esc

F1,输入 write

在命令选择器中查找 write
在命令选择器中查找 write

按第一次 Enter,让命令行形成 :write

再按一次 Enter 保存。

保存后的 Data model 段落
保存后的 Data model 段落

后面的跨文件搜索练习,就要用到这三个字段。

关闭一次 Picker

  1. Space /
  2. 输入 pocket
搜索 pocket 查看多条项目结果
搜索 pocket 查看多条项目结果
  1. 按几次 Ctrl+n 看看不同文件的预览。

  2. Esc

取消 Picker 后返回原来的 README
取消 Picker 后返回原来的 README

文件和光标都会留在打开 Picker 之前的位置。如果发现搜错了,直接按 Esc 取消就行。

Space // 的分工

场景按键搜索范围
已知道内容在当前文件/文字 Enter当前 Buffer
只知道它在项目某处Space /,再输入文字当前项目

Space / 底层用的是 ripgrep,也会遵守项目的 Git ignore 规则,所以搜索结果里通常不会混进构建产物。

3. 工作流二:选择部分结果并加入 Quickfix

搜索 Picker 适合找到一处就马上打开。如果想连续查看好几个结果,可以把它们放进 Quickfix,让结果列表一直留在底部。

这一轮新增:

按键效果
Tab选中当前 Picker 结果,并移到下一条
Ctrl+q把已选结果加入 Quickfix;没有选中项时会加入当前全部结果
] q / [ q跳到下一条 / 上一条 Quickfix 项

只挑两条 tasks

  1. Space /,输入 tasks
搜索项目中的 tasks
搜索项目中的 tasks
  1. Ctrl+n / Ctrl+p 找到一条想保留的结果。

  2. Tab。当前行出现选中标记,高亮自动移到下一条。

  3. 再选一条,按 Tab

继续选中 Complete tasks
继续选中 Complete tasks
  1. Ctrl+q

Picker 会关闭,底部随即打开 Quickfix 窗口。刚才明确选了两项,所以这里也只有两条。Quicker 会分别给文件名、行号和源代码加上样式,看起来更清楚。

把两条选中结果加入 Quickfix
把两条选中结果加入 Quickfix

在 Quickfix 中逐项查看

  1. Quickfix 刚打开时,焦点在底部。按 j / k 选择其中一条。
  2. Enter。对应文件会在上方编辑窗口打开,光标跳到命中位置;底部 Quickfix 列表继续保留。
打开第一条 Quickfix 结果
打开第一条 Quickfix 结果
  1. 在上方代码中按 ] q,跳到下一条。
用右方括号 q 跳到下一条结果
用右方括号 q 跳到下一条结果
  1. [ q,返回上一条。

Quickfix 可以带着你在不同文件之间来回跳。上方窗口显示当前结果对应的代码,底部的结果列表则会一直留着。

检查完成后,按 F1,输入 cclose

在命令选择器中查找 cclose
在命令选择器中查找 cclose

连按两次 Enter 关闭 Quickfix 窗口。

关闭 Quickfix 后恢复单窗口
关闭 Quickfix 后恢复单窗口

4. 工作流三:在 Quickfix 里跨文件改名

Quicker 允许直接编辑 Quickfix Buffer,改结果行就跟改普通文本一样。

保存 Quickfix 会立即写回源文件

在 Quickfix 里按 u,可以撤销还没保存的编辑。一旦保存,改动就会写回源文件。前面留下的 Git 基线,就是拿来检查或恢复这些改动的。

completed 批量改成 done

  1. Space /,输入 completed
  2. 确认结果包含 model.py 中的字段和 README 中的列表项。
搜索 completed 的两处结果
搜索 completed 的两处结果
  1. 这次不按 Tab,直接按 Ctrl+q。没有手动选中项时,当前全部结果都会进入 Quickfix。
把 completed 的全部结果加入 Quickfix
把 completed 的全部结果加入 Quickfix
  1. 在底部 Quickfix 中输入 /completed,按 Enter。这里的 / 搜索当前 Quickfix Buffer。
  2. c i w,输入 done,按 Esc
先把一处 completed 改成 done
先把一处 completed 改成 done
  1. n 到下一个 completed,再按 . 重放刚才的修改。

这时改动还只在 Quickfix Buffer 里,底部状态会提示它已经修改。先把两行都看一遍,确认两处都变成了 done。如果改错,就按 u 撤销,修好再继续。

用点命令完成两处 Quickfix 改名
用点命令完成两处 Quickfix 改名

确认无误后:

  1. F1,输入 write

  2. Ctrl+n / Ctrl+p 选中准确的 write 命令。

  3. 连按两次 Enter。第一下把命令送到底部命令行,第二下执行写回。

Quickfix 写回源文件后两处均为 done
Quickfix 写回源文件后两处均为 done

这次保存会让 Quicker 正式应用修改。按照当前设置,没有其他未保存内容的源 Buffer 会同时写入磁盘;事先已经修改过的源 Buffer 则会继续保持“已修改”状态,仍需单独保存。

F1,输入 cclose

连按两次 Enter 收起底部窗口。

再按 Space /,搜索 completed

搜索 completed 已无结果
搜索 completed 已无结果
  • 应该没有任何结果;
  • Esc 关闭无结果的 Picker;
  • 重新按 Space /,搜索 done,应看到 README 和 model.py 的两处结果。

5. 工作流四:从 Quickfix 删除条目,再修改源文件

眼下这个小工具还用不上 created_at。它一共出现在两行:

  1. model.py 的字段定义;
  2. README 的字段列表。

这一轮要分清两种“删除”:在 Quickfix 里按 d d,只是去掉一条搜索结果;只有在源码 Buffer 里按 d d,才会真的删掉代码行。

  1. Space /,输入 created_at
  2. 确认 Picker 正好有上面两条结果。如果数量更多,请通过 Tab 只选这两条。
搜索 created_at 的两处结果
搜索 created_at 的两处结果
  1. Ctrl+q 加入 Quickfix。
把 created_at 的两处结果加入 Quickfix
把 created_at 的两处结果加入 Quickfix
  1. g g 到 Quickfix 第一行。
  2. 看清当前行的文件名,按 d d
从 Quickfix 删除第一条结果
从 Quickfix 删除第一条结果

当前结果会从底部的 Quickfix 列表里消失。按 u 可以把这一条找回来。

用 u 恢复被删除的 Quickfix 条目
用 u 恢复被删除的 Quickfix 条目

再按 d d 删除同一条目。

检查完以后,按 F1writeEnterEnter。这次保存只会更新 Quickfix 列表,从列表里删掉结果,并不会连带删除源文件里的那一行。

F1ccloseEnterEnter。再用 Space / 搜索 created_at,两份源文件里的结果应该都还在。这里一定要分清:修改 Quickfix 行里的文字会写回源文件,删除整条 Quickfix 结果却只会改变列表本身。

现在回到真正的源码删除流程:

  1. 在当前搜索 Picker 中选中 model.py 里的字段结果。
删除源文件前确认 created_at 的剩余结果
删除源文件前确认 created_at 的剩余结果
  1. Enter 打开它。

  2. 光标落到 created_at: datetime 后按 d d

  3. 输入 /from datetime,按 Enter,再按 d d 删除已经无用的导入。

删除 model.py 中的字段和无用导入
删除 model.py 中的字段和无用导入
  1. F1,输入 write

  2. 连按两次 Enter 保存 model.py

  3. Space /,再次搜索 created_at;此时只应剩 README 的列表项。

created_at 只剩 README 一处
created_at 只剩 README 一处
  1. Enter 打开它,光标落在 - \created_at`` 上后按 d d
从 README 删除 created_at 列表项
从 README 删除 created_at 列表项
  1. F1writeEnterEnter 保存 README。

最后用 Space / 搜索 created_at,应得到空结果。

最终搜索 created_at 已无结果
最终搜索 created_at 已无结果

如果还有结果,按 Enter 打开,先把文件和上下文看清楚,再决定要不要删。

再搜索 class Task,确认模型类仍然完整。

最终搜索 class Task 的结果
最终搜索 class Task 的结果

使用 dd 前先确认当前 Buffer

在 Quickfix 里,d d 删除的是结果条目;在源码 Buffer 里,d d 删除的才是代码行。动手前先确认自己在哪个窗口。

6. 结果检查

Space / 搜索 class Task,按 Enter 打开 model.py。它现在应该是:

python
from dataclasses import dataclass

@dataclass(slots=True)
class Task:
    title: str
    done: bool = False

README 末尾应该是:

markdown
## Data model

A task stores:

- `title`
- `done`

F1wallEnterEnter,再把所有文件保存一次。这一章的改动可以先留在 Git 工作区,等第 11 章再统一处理。

本章肌肉记忆

目标按键
项目全文搜索Space /
在 Picker 中上下选择Ctrl+n / Ctrl+p
打开结果 / 关闭 PickerEnter / Esc
选中多条 Picker 结果Tab
把已选项或全部结果加入 QuickfixCtrl+q
前后遍历 Quickfix[ q / ] q
将 Quicker 编辑应用到源文件在 Quickfix Buffer 中保存
从 Quickfix 移除当前结果Quickfix 中按 d d;源文件保持原样
关闭 Quickfix 窗口F1ccloseEnterEnter

上一章:包围与注释 · 下一章:Buffer、分屏与 Tab

本文档采用 知识共享 署名-相同方式共享 4.0 协议 进行许可。