Yank Note 系列 11 - 预览内查找功能

121 阅读4分钟

Yank Note 是我编写的一款面向程序员的笔记应用。这里我将会写下一些关于 Yank Note 的文章

同样“自定义快捷键”功能一样,预览内查找功能也很早之前就有用户希望 Yank Note 能有这个功能,这个功能也迟迟没做上。之前也调研过一些方案,都不太满意。不过,一个不完美的功能,能满足用户部分需求,那么也还是可以先做上。

功能期望

首先说一下我理想中的“预览页内查找”功能是怎样的。

img-20230812175730.png

类似浏览器的“页内查找”功能,需要

  1. 支持展示匹配数量和当前定位的序号
  2. 支持向上/向下查找
  3. 可以高亮当前和已匹配的文字
  4. 滚动条可以显示已匹配文字的位置

除此之外,还有一些

  1. 支持大小写敏感
  2. 支持全字匹配
  3. 支持正则表达式

一些方案

要实现这个功能,目前只能分为两个方向:使用 Electron 的方法,或者基于 Web 接口。

1. 基于 Electron

浏览器的“页内查找”功能已经做得比较好了,而 Yank Note 是基于 Electron 的,应该也能有这样的接口可以来实现“页内查找吧”。

翻 Electron 文档确实找到了一个 findInPage Api

不过这个 API 有一些局限性,那就是真的只能实现“页内查找”。Yank Note 文档是放到 iframe 内渲染的,强行使用那就只能是这个效果。

img-20230812180804.png

于是我开始找有没有能在 iframe 搜索的 Electron 接口。

幸运的是,VSCode 开发团队也遇到了这个问题,这里有个 Issue 讨论了在 Electron 中实现 iframe 页内查找的方法。

VSCode 团队确实比较牛逼,发现 Electron 没有这个功能,直接改造 Electron,加上了 findInFrame/stopFindInFrame 方法,实现了在 iframe 中查找的功能。

体验了一下 VSCode,确实效果已经很不错了。

img-20230812181524.png

不过遗憾,上面的 PR Electron 也没有合并,翻看 VSCode 源码虽然确实用的 findInFrame/stopFindInFrame 方法,但VSCode 发行版的 Electron 是自己内部的一个分支,并未对外开放

我下载了 VSCode 的开源版本VSCodium - Open Source Binaries of VSCode,发现这个功能根本就不能使用,完全的残废。。。

2. 基于 Web 接口

基于 Web 来做的话,基本只有使用 window.find 来做,其他方案如自己做搜索匹配和高亮,实现起来都会非常复杂且通用性不高。

这个接口还是有不少缺点:

  1. 不能高亮所有的匹配项
  2. 滚动条不能显示匹配位置
  3. 不能获取匹配数量
  4. 不能指定和显示匹配位置
  5. 非标准接口,但是兼容性还行
  6. 当前匹配项是选中状态而不是 highlight 状态

VSCode 的网页版本,也是用的这个接口来做的,只能说功能比起上面的就差太多了,而且也有些小问题,比如上下箭头激活状态不正确。

img-20230812182536.png

不过,也有用这个接口做得比较好的产品,如 Chrome 的 Vimium 插件,就是用这个接口实现了页内查找功能,能展示匹配数量且支持正则表达式。

img-20230812183351.png

看源码,他能展示匹配数量和支持正则表达式的原理是从 document.body.innerText 自己做匹配,计算出每一处的匹配字符串和数量,拿着每一处的匹配字符串调用 window.find 来实现页内查找功能。

当然这个方案的缺点就在于用 document.body.innerText 匹配的东西不一定和 window.find 一致。因此也没法实现展示当前匹配位置的功能。然后就是有时候虽然有匹配项,但点击查找也会“踏空”。

至于高亮状态,他用了一个很巧妙的方式来解决:在调用 window.find 时候给页面设置一个样式类,使用 ::selection 选择器来高亮匹配项。接着监听 selectionchange 事件,移除这个样式类。

最终方案

考虑如下几点

  1. Electron 上游的 findInFrame/stopFindInFrame 还不可用
  2. 自己编译一套 Electorn 成本太高
  3. Yank Note 需要支持在浏览器中使用

所以排除使用 Electron 的方案。聚焦参考 Vimium,使用 window.find 来实现。

效果

img-20230812184449.png

具体实现情况:

  1. 支持展示匹配数量和当前定位的序号:只支持展示匹配数量,且不一定准确
  2. 支持向上/向下查找:支持
  3. 可以高亮当前和已匹配的文字:不支持
  4. 滚动条可以显示已匹配文字的位置:不支持
  5. 支持大小写敏感:支持
  6. 支持全字匹配:不支持
  7. 支持正则表达式:支持

虽然还是没完全达到理想中的效果,但至少功能有了,能满足一部分需求,还是很不错了。