Pragtical 定制笔记

前言

在 文本编辑器的基础方程式 我提到从 Nodepad++ 切换到 lite-xl fork Pragtical。事实证明这个选择非常明智,两年下来体验挺好,就是有的默认值不太符合我的预期,但肯定比一堆 CVE 的 npp 好用,也就一直凑合用。

却说五月写完 2025 年度总结 - 游戏篇 就一直没发新文章,旧文倒是一直在更新,这段时间也一直没闲着:

  • 六月正式开坑金庸全集,目前看了八本,还有六部大长篇。随着 Chromium 彻底弃用 ManifestV2 和 cromite 开发停摆,也顺带更新和重构了插件和脚本。
  • 七月重构了手头自托管的停更项目,写了 pt2json 转换 ProcrastiTracker 数据库,还完成了游戏数据库 SQL 查询的 Lean 形式化证明,这里先按下不表。
  • 八月底也终于开始拖了几年的 Beancount v3 迁移,重构 importer,也追溯了部分历史账单。
  • 解决悬而未决的历史遗留问题后,九月终于得闲大刀阔斧改造 IDE,先更新了一波 Obsidian 插件,还手搓了几个,替换万能没更新的老古董。

说了一大堆,终于进入正题,翻新 Obsidian 插件后,我也终于着手改造 pragtical,移植了不少 Obsidian/VSC/Zed 插件,优化编辑体验。结束后大概会继续改造 zed,这又是后话。

由于 pragtical 几乎所有功能都通过插件实现,我的改动也比较多,下面按照功能分组,不一定是实际的定制顺序。

为了省去维护超链接的麻烦,下文插件均为 plaintext,感兴趣可以在对应软件的插件仓库自行搜索。部分私有插件我没加到 pragtical/plugins,建议自己动手丰衣足食。

定制

VSC files.exclude: Settings -> Core -> General -> Ignore files,这里要写 Lua Pattern。

基于 openselected/smartopenselected 改造,支持双链笔记的 wikilinks,因为 pragtical 提供了 project search,比想像中简单。本来想注册 ctrl + 鼠标左键单击快捷键打开,但这样会和 link-opener 冲突,于是在后者加了 wikilinks integration,更自然,也不用合并插件。

  • 默认快捷键是 ctrl + alt + 鼠标左键,太别扭,改成自然的 ctrl + 鼠标左键
  • 删除超链接下划线,preview 可以显示,source view 还一堆下划线看着有点恼人
  • 大概是因为我卸载了自带的 Edge,common.open_in_system(url) 在 Windows 不能正常使用,改为检测 BROWSER 环境变量,没有再 fallback 到 PATH 中的 chromium/palemoon
  • 还加了上面提到的 wikilinks 集成,根据 context 判断鼠标指针下的文本,链接和 wikilinks 都能通过 ctrl + 鼠标左键点击打开

Exec

原版只考虑了 Linux,做了 Windows PowerShell 兼容,然鹅 PowerlessShell 运行死慢,而且不能异步返回结果,等待结果会卡很久。虽然能换成 nushell,但语法又要改,太麻烦。

尝试移植过 Obsidian Templater,但我的 Windows 环境基本都是离线状态,在 Linux 可以直接用 Obsidian,没卵用,完成初版即废弃。

Toolbarview

因为 toolbarview 和 treeview 是一起的,toolbar 塞了一堆我不用的按钮会导致 treeview 空出一大截,很不爽,于是去掉我只用快捷键解决的操作(新建/打开/保存),添加 Treeview: Toggle 自定义命令。

自定义本身很简单,麻烦的是这里的 symbol。看 data/core/style.lua 是定义在捆绑的 data/fonts/icons.ttf,让 GPT-6-luna 写了基于 Python fonttool 库的 cmap HTML 预览脚本,然后手动复制内容到空文档,即可查看字符映射。

自定义命令也要翻对应插件的源码才能知道,简单组合即可。 这个自定义本来以为很简单,没想到难度反而是最高的= =

Treeview

在右侧补充 file count,逻辑和 Obsidian File Explorer Note Count 一致,同时遵守原本的 show_hidden/show_ignored 设置。

本来做了一系列优化,然鹅因为默认值是 false 还以为改炸了,直接 revert,后面发现时又不想重做,凑合用……

Minimap

和 ToC 功能重合,加了根据文件后缀名禁用的功能,在 Markdown 文件隐藏 minimap。

Gitdiff_highlight

原版在非 git 文件夹会进行不必要的重复扫描,在非预期的 DocView(比如 DIY 的 ToC View)还会报错,fork 之后修复了这两点,顺带优化 minimap 集成。

语法

Registry

这是我最早尝试改造的部分,搓的第一个 pragtical 插件 language_registry.lua 添加 Windows 注册表文件的语法高亮,看时间戳已经是遥远的 2024-09。这里遇到了第一个坑,pragtical 当时编辑带 BOM 的文件会自动移除 BOM,导致虽然能在 pragtical 查看,但实际编辑还得用其他编辑器,很蠢……Anyway,开了 issue 后开发者很快就加了 BOM 支持,现在没这问题。

Rainbow-CSV

第二个插件 rainbow_csv.lua 因为对 pragtical API 不太熟悉,前前后后做了三版才完美实现。参考了 VSC/Zed 对应插件,对 CSV/TSV 按照分隔符用不同的颜色高亮,很实用,维护 game-stats 必备。这个插件算是原理很简单,效果很酷炫。

Markdown

后面就是 language_md.lua 的自定义,其实本来想移植 Obsidian Dynamic Highlight 插件,2026-04 用 DeepSeek-V4-Pro 做过,但完全不能用,这个月早些时候也让 GPT-5.6-luna 试过,这个版本倒是能高亮文字,但是有重影,实现原理就有问题。最后还是过了一星期突然顿悟:因为只适用于 Markdown,其实不需要拘泥于独立的插件,直接改造现有的 Markdown 语法高亮就能实现。

可惜研究下来 pragtical 并不支持 tree-sitter query,附带的 regex 库也有很多功能不支持,只能写丑陋的 Lua Pattern,好在起码能用。概念验证成功后花了几天陆续移植完了我使用的 Dynamic Highlights 规则,color palette 也完全一致。

优化是最痛苦的部分,Lua Pattern 非常垃圾,alternation 都冇,只能重复写很多遍……也不能定义字典再用函数生成,因为 patterns 里面的顺序很重要……最后还是找到 langauge_c.lua 里面的 regex 发给 GPT-5.6-luna,改了一份能用的 regex 才最终解决……Anyway,精简后源码非常清爽。

搞定了 Dynamic Highlights,后面又开始移植 Obsidian 的其他样式: links (hyperlink, wikilink, footnote), checkbox, inline metadata。tag 没能 100% 复刻,但不加半透明背景也挺好看,凑合用。

随后补充 code block language identifier,步骤:

  1. 将 C/V/R 这种单个字符的 identifier 挪到最后,防止误匹配
  2. 列出 pragtical 支持的全部语言
  3. 对比 PrismJS (Obsidian), Chroma (Hugo), Rouge (Jekyll) 用的 identifiers,Linguists (GitHub) 太多了懒得看
  4. 补充

这部分也很痛苦,因为实在太多,116 个语法插件里看了十几个就累了,只加了四十个多。

最后心想既然都做到这儿了,送佛送到西,顺带交了 PR 合并除了 Dynamic Highlight 和 Obsidian style 的修改,后面又加了 YAML/TOML/JSON frontmatter 支持,基本完美。

移植

移植插件,「移植」的只是功能,并没有参考肮脏的 JavaScript/TypeScript 源码,大部分是 Obsidian/VSC/Zed。

Indent-Rainbow

参考 indentguide 和 vscode-indent-rainbow 搓了两个版本:

  • 一版使用竖线和 core.syntax,配置 3px offset 之后还挺好看的,但仅限 JSON/TOML/YAML 这种,在 JavaScript/Python 这种关键字多的语言花花绿绿一大片,不是很好看。而且用于对齐的 3px offset 是硬编码的,如果是 monospace font 可能就对不齐,逼死强迫症
  • 一版填充带透明度的色块和 core.common.color,侵入性没那么强,配色也更自由

初版代码很优雅,但创建新文件/打开空文件会报错,只能加了十多行检查,有点丑陋。 后面又加了配置项,完成度更高,也更丑陋。 本来两个版本是独立的,后面还是合并到一起,通过 style 配置。

Freeze-Row

类似 Excel 的冻结首行,还是给 CSV/TSV 用。 本来也想做冻结首列,但没能成功,首列的文本就是不显示…… 「首行」很简单,因为 EOL (LF/CRLF) 是天然的分隔符,分行逻辑一目了然。 「首列」debug 半天才想起来这玩意儿应该只支持 tabular format,所以要解析分隔符。 然鹅试了几种办法都不行,后面只多加了几个 delimiter 支持,优化了性能,先凑合用。 大概写一个 invert-table 插件,转置表格,配合 freeze-row 曲线救国更有希望…… 或者干脆写一个 csv-preview 插件,直接生成表格预览,anyway,这些都是后话。

Calendar

参考 Tree View 实现的 Calendar View。

使用体验和 Obsidian 的 Calendar + Periodic Notes 一致:

  • 点击日期/星期/月份/年份打开日/周/月/年记
  • 切换月份按钮、点击返回当前月份的 TODAY 按钮
  • Ctrl+T 打开今日日记
  • 弱化显示其他月份的日记
  • 以星期日作为一周的第一天,标题使用大写的三个首字母,完全一致的 ISO week 算法(星期一到星期六正常,星期日使用「下一个」星期四的 ISO week)
  • 支持配置 Periodic Notes 格式,在 Linux/Windows 使用不同路径
  • 只在 project 路径包含 Obsidian 时显示,默认不加载
  • 新建笔记时在 command view 提醒,默认不创建文件
  • 因为我在 2026-04 从周记迁移到半月记,隐藏 2026-W15 之后的偶数周
  • 还复刻了年末 W52 和次年 W01 之间的路径 bug,就两三天有影响,遇到再修

遗憾的是由于没有全文索引,不能根据文本量在日期下方显示点点。第一次尝试遍历实现统计正常但点点很小,改大了也很丑,第二次改为绘制 ●○,大小正常但统计有点毛病,而且和 Obsidian 一样周记/日记对不齐,战略性放弃。

ToC

在 Calendar View 下方显示 Markdown ToC,配合 Tree View 实现和 Obsidian 几乎一致的 UI。缺少的左侧 ribbon 和两侧 sidebar 可以用 Toolbar/Command View 替代。

Rldates

类似 Obsidian 的 Natural Language Dates (nldates) 插件。

#前言 提到我手搓了几个 Obsidian 插件,这是其中一个。因为我只需要 nldates 的 Date Autosuggest 功能,基于原版插件大幅精简,写了 Relative Dates (rldates),按 @ 插入「相对」日期。这里和 Calendar View 做了联动,只在 Obsidian vault 插入 wikilinks,其他时候触发只插入单纯的日期。

因为 Obsidian 插件就是我写的,一开始拘泥于完整逻辑的移植,写了两版都不行,第三版参考 datetimestamps 显示在 command view,效果很完美,也更贴合 IDE 的逻辑。

Thief-Book

状态栏看小说,受 Thief-Book-VSCode 启发,额外支持鼠标滚轮翻页。唯一的缺点就是为了避免写入权限只用了内存缓存,pragtical 关闭后重新打开不能恢复阅读进度。为了弥补这点加了页码跳转的功能,对娱乐插件而言够用。

Privacy-Glasses

灵感来自 Obsidian 同名插件,不过原插件作者已经删库跑路,现代替代大概是 Privacy Screen。 本质上和 indent-rainbow 没有区别,只是改了表现形式。 实现很取巧的一点是默认调用 style.background 定义的背景色,自适应主题。

Vinfall's Geekademy

Sine īrā et studiō