ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Markdown LSP 集成指南:为 QOwnNotes 编辑器接入语言服务器

Markdown LSP 集成指南:为 QOwnNotes 编辑器接入语言服务器
  • 桌面应用

【免费下载链接】QOwnNotes

QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.

项目地址:https://gitcode.com/gh_mirrors/qo/QOwnNotes
点击查看免费下载

QOwnNotes 在笔记编辑器中集成了Markdown 语言服务器协议(LSP)客户端,使纯文本笔记编辑器获得诊断(lint)、代码动作(quick fix)、补全和文档格式化等"IDE 级"编辑能力。本文以 QOwnNotes 官方文档为主体,结合仓库内 markdownlspclient.cpp、markdownlspdocumenttracker.cpp、markdownlspsettingswidget.cpp 等源码,完整讲解 LSP 服务器的安装、配置、底层协议流程与日常使用,读完后你可以将 Marksman 或 Rumdl 接入编辑器,并用上彩色波浪下划线、右键快速修复、wiki 链接补全和格式化功能。

Markdown LSP 能带来什么

启用 Markdown LSP 后,QOwnNotes 编辑器在保留原有纯文本 Markdown 编辑体验的同时,获得以下增强能力:

  • 对检测到的 Markdown 问题与规则违规显示彩色波浪下划线(不同严重级别对应不同颜色)
  • 在右键上下文菜单中为诊断提供Code Actions(服务器建议的快速修复)
  • 提供忽略规则(Ignore rule)操作,可永久屏蔽某条具体诊断规则
  • 提供补全弹窗,支持笔记链接及服务器给出的其他建议(例如与 Marksman 配合的 wiki-link 补全)
  • 通过上下文菜单提供Format Document(格式化文档)与Format Selection(格式化选区)操作
  • 支持增量文档同步,与服务器通信更高效
  • 提供详细日志模式,便于调试服务器通信

这些能力全部发生在QOwnNotesMarkdownTextEdit编辑器组件内部(qownnotesmarkdowntextedit.cpp),笔记内容仍以纯文本形式存储,不引入任何私有格式。

支持的 LSP 服务器

仓库内置了两款开箱即用的 Markdown LSP 服务器,分别覆盖"导航补全"与"lint 规则"两类场景:

服务器命令说明
Marksmanmarksman默认服务器;提供 wiki-link 补全和跨笔记导航
Rumdlrumdl server提供类似markdownlint的 Markdown 规则检查

从 markdownlspsettingswidget.cpp 中可以看到,自动检测逻辑正是按marksman与rumdl两个可执行文件在PATH中查找的:

const QVector<KnownMarkdownLspServer> kKnownMarkdownLspServers = { {"Marksman", "marksman", {}}, {"Rumdl", "rumdl", {QStringLiteral("server")}}, };

除此之外,任何通过stdio通信、兼容 LSP 的 Markdown 服务器,都可以手动配置命令后使用。

安装 LSP 服务器

安装 Marksman

Marksman 提供 wiki-link 补全、跨笔记导航和 Markdown 项目诊断,与 QOwnNotes 的 wiki 风格笔记链接配合效果很好。

  • Linux/macOS:从 GitHub Releases 页下载,或通过包管理器安装(macOS 上可用brew install marksman,NixOS 上可用nix-env -iA nixpkgs.marksman)
  • Windows:从 GitHub Releases 页下载.exe,放入PATH中的任意目录

安装完成后,在设置中使用Auto-detect(自动检测)即可自动定位。

安装 Rumdl

Rumdl 是一个快速的 Markdown linter,强制可配置的风格规则,并可作为 LSP 服务器运行。

通过 Cargo(Rust)安装:

cargo install rumdl

通过 pip 安装:

pip install rumdl

下载预编译二进制:Linux、macOS 和 Windows 的预编译二进制可从 Rumdl 的 GitHub Releases 页面获取。

安装后,在设置中将Command设为rumdl,Arguments设为server。

在 QOwnNotes 中启用与配置

打开设置(Settings),进入Editor -> Markdown LSP页面(对应设置组件 markdownlspsettingswidget.ui):

  1. 勾选Enable Markdown language server (LSP)启用功能
  2. 在Command字段填写 LSP 服务器的可执行文件名或完整路径,默认值为marksman
    • 点击Auto-detect会在你的PATH中自动搜索marksman或rumdl并填入命令字段
  3. 可选:在Arguments中填写传给服务器进程的参数(多个参数以空格分隔),例如 Rumdl 必需的server
  4. 勾选Show all LSP server log output in debug log开启详细诊断输出
  5. 点击Test Connection验证 QOwnNotes 能否启动服务器,并针对当前笔记文件夹完成 LSP 初始化握手
  6. 使用Reset ignored rules清除之前所有被忽略的诊断规则 ID

设置页还提供了状态标签,实时显示解析后的命令路径、参数以及当前是否被禁用(参见updateStatusLabel实现)。

配置项与底层存储键

从 applyMarkdownLspSettings 与 markdownlspsettingswidget.cpp 的读写逻辑可以确认,这些界面选项对应以下 QOwnNotes 设置存储键:

界面选项设置键默认值
Enable Markdown language server (LSP)Editor/markdownLspEnabledfalse
CommandEditor/markdownLspCommandmarksman
ArgumentsEditor/markdownLspArguments空列表
Verbose loggingEditor/markdownLspVerboseLoggingfalse
Ignored rulesEditor/markdownLspIgnoredRules空列表

当Command或Arguments发生变化时,代码会检测到serverConfigurationChanged,自动关闭并重启已运行的服务器进程,同时重新打开当前文档(保存并恢复 URI 与文本),无需手动重启 QOwnNotes。

工作原理:一次完整的 LSP 会话

从源码层面看,整个集成由三部分组成:

  • MarkdownLspClient(markdownlspclient.h):负责通过QProcess启动服务器进程、编码/解码 LSP 消息、维护请求 ID 与回调分发
  • MarkdownLspDocumentTracker(markdownlspdocumenttracker.h):监听QTextDocument::contentsChange,把每一次编辑转换为didChange负载
  • QOwnNotesMarkdownHighlighter(qownnotesmarkdownhighlighter.cpp):把服务器推送的诊断渲染为彩色波浪下划线

生命周期与消息流

  1. 启动与握手:启用后,QOwnNotes 以当前笔记文件夹(NoteFolder::currentLocalPath())为工作区根目录启动服务器进程,随后发送initialize请求。请求中携带进程 ID、rootPath、rootUri、客户端信息(名称QOwnNotes、版本号)以及能力声明——例如声明不支持 snippet、格式化和 code action 均不做动态注册(markdownlspclient.cpp)。
  2. 打开文档:打开笔记时,QOwnNotes 发送textDocument/didOpen通知(languageId为markdown)。若初始化尚未完成,文档会先缓存在PendingDocument中,握手成功后自动补发。
  3. 编辑同步:输入过程中,变更通过textDocument/didChange转发。服务器在initialize响应中通告的textDocumentSync值决定同步方式:2表示增量同步(仅发送变更区间),否则退化为整文档同步。客户端据此设置跟踪器的同步模式。
  4. 诊断渲染:服务器通过textDocument/publishDiagnostics推送的诊断,被渲染为编辑器中的彩色波浪下划线。
  5. 右键交互:在带下划线的区域右键,会显示可用的code actions与忽略该规则选项。
  6. 补全与格式化:服务器提供补全项时以弹窗展示;文档格式化分别通过textDocument/formatting或textDocument/rangeFormatting完成。

增量同步的实现细节

MarkdownLspDocumentTracker是性能关键路径的实现核心。它在每次按键触发的contentsChange回调中保持轻量操作:

  • 维护一份行长度快照(_lineLengths),用于把文档位置转换为编辑前的(行、列)坐标,从而精确计算旧区间;
  • 累积的增量变更先放入待发队列,由防抖定时器(默认 200ms)合并后一次性发送,避免每个按键都触发一次 IPC;
  • 对超过 100000 字符的大文档,防抖间隔自动提升到至少 1000ms(见 markdownlspdocumenttracker.cpp 中的kLargeFullSyncDocumentSize与kLargeFullSyncDebounceIntervalMs常量);
  • 增量模式下的新增文本直接按字符读取,并把段落分隔符(U+2029)转换为\n,全程不调用昂贵的toPlainText()。

当服务器返回快速修复的编辑内容时,跟踪器会临时进入setSuppressed(true)状态,避免把程序化编辑当作用户输入重复上报,随后立即执行一次完整同步(flushFullSync),让诊断状态立刻刷新。

通信协议细节

客户端与服务器通过标准stdio管道通信,消息采用 LSP 规范的Content-Length帧格式(sendMessage / parseMessages):

Content-Length: <payload 字节数>\r\n\r\n<JSON-RPC 2.0 负载>

对于服务器发来的服务器到客户端请求(如client/registerCapability、window/workDoneProgress/create),客户端会主动回复空结果响应,避免服务器因等待应答而阻塞。服务器stderr中形如ERR/ERROR的行会被提取并作为错误消息反馈给界面。

日常使用指南

诊断下划线的颜色语义

诊断的渲染逻辑位于 qownnotesmarkdownhighlighter.cpp 的setMarkdownLspDiagnostics,颜色由 LSP 严重级别决定:

严重级别含义下划线颜色
1Error红色
2Warning橙色
3Information蓝色
4Hint绿色

下划线采用波浪样式(SpellCheckUnderline),与拼写检查、LanguageTool 语法检查的下划线一致,且逐字符合并基础格式,保留原有语法高亮的文字颜色;鼠标悬停时还会显示诊断消息作为 tooltip。多行诊断会自动覆盖其跨越的每一行。

右键菜单:快速修复与忽略规则

在带下划线的区域右键,QOwnNotes 会:

  1. 定位覆盖光标位置的诊断,以其消息作为子菜单标题;
  2. 向服务器发起textDocument/codeAction请求(最多等待 2 秒),把返回的 code action 逐个加入菜单——带编辑内容的动作直接应用到文档,带命令的动作通过workspace/executeCommand执行;
  3. 提供Ignore rule ... globally菜单项,将当前诊断的规则 ID 加入忽略列表。

诊断规则 ID 由source与code组成(如rumdl:MD013形式,见 Diagnostic::ruleId)。被忽略的规则持久化存储在设置键Editor/markdownLspIgnoredRules中(markdownlspignoredrules.cpp),可在设置页用Reset ignored rules一键清空。

补全

输入时若当前没有内置补全候选(wiki 链接、公式结果、单词补全、脚本autocompletion钩子等),QOwnNotes 会向服务器发送textDocument/completion请求,并把返回的补全项展示为带过滤输入框的弹窗(showMarkdownLspCompletions)。选中后以光标所在词起始位置插入补全文本并追加一个空格。若设置中开启了Editor/autocompleteApplySingleResult,当服务器只返回一个补全项时会直接应用而不弹窗。

格式化

编辑器上下文菜单中的Markdown LSP -> Format document / Format selection分别对应textDocument/formatting与textDocument/rangeFormatting。格式化请求固定使用 4 空格缩进(tabSize=4、insertSpaces=true),返回的文本编辑会按从后往前的顺序应用,避免位置偏移。应用编辑后立即触发完整同步与诊断刷新。

故障排查

  • 服务器无法启动:QOwnNotes 会在状态区域显示警告。同时MarkdownLspClient::start()会在命令为空或进程 2 秒内未启动时发出错误消息。
  • 查看通信细节:勾选Show all LSP server log output in debug log,服务器全部stderr输出与同步模式信息都会写入调试日志。
  • 验证连接:设置页的Test Connection会独立启动一个客户端、对当前笔记文件夹执行initialize,等待serverInitialized信号(5 秒超时),成功后显示"Markdown LSP connection successful"。
  • 诊断不消失:使用Reset ignored rules清除历史忽略记录;快速修复后会立即执行完整同步,不必等待防抖定时器。

连接成功后,QOwnNotes 会在状态栏短暂显示 "Markdown LSP server connected"(3 秒),表示初始化握手已完成、可以开始使用全部 LSP 能力。

相关阅读

  • Spellchecking
  • LanguageTool
  • Harper
  • Concept
  • 桌面应用

【免费下载链接】QOwnNotes

QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.

项目地址:https://gitcode.com/gh_mirrors/qo/QOwnNotes
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进