前言
在 文本编辑器的基础方程式 我提到从 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。
Exec
原版只考虑了 Linux,做了 Windows PowerShell 兼容,然鹅 PowerlessShell 运行死慢,而且不能异步返回结果,等待结果会卡很久。虽然能换成 nushell,但语法又要改,太麻烦。
尝试移植过 Obsidian Templater,但我的 Windows 环境基本都是离线状态,在 Linux 可以直接用 Obsidian,没卵用,完成初版即废弃。
Gitdiff_highlight
fork 的改动
- 避免在非 git 文件夹进行不必要的重复扫描
- 修复在非预期的 DocView(比如 DIY 的 ToC View)的报错
- 修复 git 未安装时的报错
- 优化 minimap 集成
Link-Opener
- 默认快捷键是 ctrl + alt + 鼠标左键,太别扭,改成自然的 ctrl + 鼠标左键
- 删除超链接下划线,preview 可以显示,source view 还一堆下划线看着有点恼人
- 大概是因为我卸载了自带的 Edge,
common.open_in_system(url)在 Windows 不能正常使用,改为检测BROWSER环境变量,没有再 fallback 到PATH中的 chromium/palemoon - 新增 reference style link 支持,这样才比较完整
- 还加了 wikilinks 集成,根据 context 判断鼠标指针下的文本,链接和 wikilinks 都能通过 ctrl + 鼠标左键点击打开
Minimap
和 ToC 功能重合,加了根据文件后缀名禁用的功能,在 Markdown 文件隐藏 minimap。
Toolbarview
因为 toolbarview 和 treeview 是一起的,toolbar 塞了一堆我不用的按钮会导致 treeview 空出一大截,很不爽,于是去掉我只用快捷键解决的操作(新建/打开/保存),添加 Treeview: Toggle 自定义命令。
自定义本身很简单,麻烦的是这里的 symbol。看 data/core/style.lua 是定义在捆绑的 data/fonts/icons.ttf,让 GPT-6-luna 写了基于 Python fonttool 库的 cmap HTML 预览脚本,然后手动复制内容到空文档,即可查看字符映射。
自定义命令也要翻对应插件的源码才能知道,简单组合即可。 这个自定义本来以为很简单,没想到难度反而是最高的= =
Wordcount
字符统计,CJK 字符超过设定比例显示 CJK 字符数,否则显示非 CJK 单词数+CJK 字符数。 但因为 Markdown link 的存在,实际使用时还是需要将比例设定在一个比较低的值才正常。 只做了忽略 frontmatter 和 code block 的简单处理,精确统计还是太麻烦。
语法
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,步骤:
- 将 C/V/R 这种单个字符的 identifier 挪到最后,防止误匹配
- 列出 pragtical 支持的全部语言
- 对比 PrismJS (Obsidian), Chroma (Hugo), Rouge (Jekyll) 用的 identifiers,Linguists (GitHub) 太多了懒得看
- 补充
这部分也很痛苦,因为实在太多,116 个语法插件里看了十几个就累了,只加了四十多个。
心想都做到这儿了,送佛送到西,交了 PR 合并除了 Dynamic Highlight 和 Obsidian style 的修改。
后面又加了 YAML/TOML/JSON frontmatter 和 admonition 支持,基本完美。
Rainbow-CSV
因为对 pragtical API 不太熟悉,前前后后做了三版才完美实现。 参考了 VSC/Zed 对应插件,对 CSV/TSV 按照分隔符用不同的颜色高亮。 很实用,维护 game-stats 必备。这个插件算是原理很简单,效果很酷炫。
Registry
这是我最早尝试改造的部分,搓的第一个 pragtical 插件 language_registry.lua 添加 Windows 注册表文件的语法高亮,看时间戳已经是遥远的 2024-09。这里遇到了第一个坑,pragtical 当时编辑带 BOM 的文件会自动移除 BOM,导致虽然能在 pragtical 查看,但实际编辑还得用其他编辑器,很蠢……Anyway,开了 issue 后开发者很快就加了 BOM 支持,现在没这问题。
移植
移植插件,「移植」的只是功能,并没有参考肮脏的 JavaScript/TypeScript 源码,大部分是 Obsidian/VSC/Zed。
Admonition
移植自我搓的 Hugo 主题 udon,实现 Obsidian callouts,不过效果不是很好。
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 一样周记/日记对不齐,战略性放弃。
Emoji
各种软件都有输入 :happy/:happy: 插入 emoji 的功能,pragtical 也不能少了。
借用了 GitHub gemoji 列表,补全功能也是现成的,没什么难度。
麻烦的是预处理 JSON,很多重复的别名和描述,好在有 jq 这一大杀器,处理挺方便。
Freeze-Row
类似 Excel 的冻结首行,还是给 CSV/TSV 用。 本来也想做冻结首列,但没能成功,首列的文本就是不显示…… 「首行」很简单,因为 EOL (LF/CRLF) 是天然的分隔符,分行逻辑一目了然。 「首列」debug 半天才想起来这玩意儿应该只支持 tabular format,所以要解析分隔符。 然鹅试了几种办法都不行,后面只多加了几个 delimiter 支持,优化了性能,先凑合用。 大概写一个 invert-table 插件,转置表格,配合 freeze-row 曲线救国更有希望…… 或者干脆写一个 csv-preview 插件,直接生成表格预览,anyway,这些都是后话。
Indent-Rainbow
参考 indentguide 和 vscode-indent-rainbow 搓了两个版本:
- 一版使用竖线和 core.syntax,配置 3px offset 之后还挺好看的,但仅限 JSON/TOML/YAML 这种,在 JavaScript/Python 这种关键字多的语言花花绿绿一大片,不是很好看
- 一版填充带透明度的色块和 core.common.color,侵入性没那么强,配色也更自由
初版代码很优雅,但创建新文件/打开空文件会报错,只能加了十多行检查,有点丑陋。
后面又加了配置项,完成度更高,也更丑陋。
本来两个版本是独立的,后面还是合并到一起,通过 style 配置。
MDFold
参考 codefold 逻辑和样式,支持折叠 Markdown 文件的 heading/admonition。
MDLint
参考 Obsidian Linter 实现的 Markdown 格式化插件。
每项功能都有单独命令,也可以一次性执行全部。
运行结束用 core.log 输出增删字符统计,方便 debug。
功能:
- YAML frontmatter: 转义,排序,补充缺省项,根据文件时间戳自动更新
- Table: 统一 pipe/column 风格,对应 markdownlint MD055/MD060 两条规则
[table-pipe-style]
"style" = "leading_and_trailing"
[table-column-style]
"aligned_delimiter" = true
"style" = "compat"
- Link: 转换 inline links 为 reference style links,并根据 URL/path 智能生成 title
Privacy-Glasses
灵感来自 Obsidian 同名插件,不过原插件作者已经删库跑路,现代替代大概是 Privacy Screen。 本质上和 indent-rainbow 没有区别,只是改了表现形式。 实现很取巧的一点是默认调用 style.background 定义的背景色,自适应主题。
虽然设计之初是为了保护隐私,如果加上隐藏标题栏和文件名的功能,再手动隐藏 TreeView,就变成了 zen mode,二者分界并不明显。
Rldates
类似 Obsidian 的 Natural Language Dates (nldates) 插件。
#前言 提到我手搓了几个 Obsidian 插件,这是其中一个。因为我只需要 nldates 的 Date Autosuggest 功能,基于原版插件大幅精简,写了 Relative Dates (rldates),按 @ 插入「相对」日期。
这里和 Calendar View 做了联动,只在当前 project 是 Obsidian vault 并且是 Markdown View 时插入 wikilinks,其他情况触发只插入单纯的日期。
因为 Obsidian 插件就是我写的,一开始拘泥于完整逻辑的移植,写了两版都不行,第三版参考 datetimestamps 显示在 command view,效果很完美,也更贴合 IDE 的逻辑。
默认 Shift + 2 触发,除了相对日期还有 at 候选,用于输入正常的 @。
Thief-Book
状态栏看小说,受 Thief-Book-VSCode 启发,额外支持鼠标滚轮翻页。 缺点是为了避免写入只用了内存缓存,pragtical 关闭后重新打开不能恢复进度。 为了弥补这点加了页码跳转的功能,对娱乐插件而言够用。
ToC
在 Calendar View 下方显示 Markdown ToC,配合 Tree View 实现和 Obsidian 几乎一致的 UI。缺少的右侧 sidebar 可以用 Toolbar/Command View 替代。
Wikilinks
- 基于 openselected/smartopenselected 改造,支持双链笔记的 wikilinks
- 原版 smartopenselected 是用于打开 NodeJS/Golang 这种源码的 import,默认打开全部匹配项。因为我主要在 Markdown 使用,加了
matchoption,默认是first,先用 exact match 打开第一个结果,没有再 fallback 到 fuzzy match。all则沿用原版插件逻辑,默认打开全部匹配项 - 支持格式
- 普通:
[[hacking-pragtical]],[[folder/Note]] - 带别名:
[[2025-11|Nov]],[[2025-11\|Nov]] - 带 heading:
[[Note#H2]],[[Note#H3|Alias]] - tag:
#tag,在 project search 搜索,类似 Obsidian 的效果 - anchor:
^1234,^a37b51,同上,便于快速删除某个 anchor
- 普通:
- 虽然支持打开带 heading/anchor 的 wikilinks,但打开后跳转到对应位置需要魔改 projectsearch,还没搞定
- 本想注册 ctrl + 鼠标左键单击快捷键打开,但这样会和 link-opener 冲突,于是在后者加了 wikilinks integration,更自然,也不用合并插件
