☰
QOwnNotes 集成 LanguageTool:在 Markdown 笔记编辑器中启用语法与风格检查
2026/10/12 2:21:42 网站建设 项目流程
  • 桌面应用

【免费下载链接】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 与 Nextcloud / ownCloud 集成的纯文本笔记与待办管理工具。除了内置的拼写检查(基于 Sonnet + Hunspell),它还可以通过本地或远程 LanguageTool 为骨架,结合 QOwnNotes 的源码实现,完整讲解功能特性、设置面板的每一项配置、底层工作流程与交互细节,帮助你从"会用"进阶到"理解其原理"。

功能特性一览

LanguageTool 集成是对现有拼写检查的补充而非替代——Sonnet 负责单词级拼写错误,LanguageTool 负责语法、风格、标点与排版层面更复杂的规则。启用后你可以获得:

  • 彩色下划线:语法、风格、标点、排版问题以不同颜色的波浪下划线标注在 Markdown 编辑器中;
  • 上下文菜单建议:右键点击带下划线的文本,可直接选择替换建议;
  • More info...链接:对提供额外文档的规则,可一键打开规则说明页面;
  • Ignore this rule动作:对不想再看到的规则可以忽略,且忽略状态会被持久化保存;
  • 快速开关:在Spelling(拼写)菜单中通过Check grammar with LanguageTool勾选项随时启用或停用。

先决条件:构建时开关

需要说明的是,LanguageTool 支持是可选的,取决于你的构建是否启用了该功能。仓库中通过编译宏LANGUAGETOOL_ENABLED控制:

  • CMake 构建时,src/CMakeLists.txt 定义了option(LANGUAGETOOL_ENABLED "Build with LanguageTool support" ON),并在第 77-79 行注入target_compile_definitions(QOwnNotes PUBLIC LANGUAGETOOL_ENABLED);
  • qmake 构建时,src/QOwnNotes.pro 中直接DEFINES += LANGUAGETOOL_ENABLED。

从源码结构看,LANGUAGETOOL_ENABLED宏包裹了 LanguageToolChecker、LanguageToolClient、编辑器集成、高亮渲染与设置面板等全部相关代码。若你的构建未启用该宏,设置面板中不会出现对应选项,主窗口的菜单项也会被隐藏(见 src/mainwindow.cpp)。因此使用前请确认你的 QOwnNotes 版本为启用了 LanguageTool 功能的构建。

配置 LanguageTool

打开Settings -> Editor(设置 -> 编辑器),在LanguageTool分组框中进行配置。该界面由 languagetoolsettingswidget.ui 定义,逻辑由 languagetoolsettingswidget.cpp 实现,并随设置对话框一起初始化与读写(见 src/dialogs/settingsdialog.cpp、src/dialogs/settingsdialog.cpp)。

启用开关

勾选Enable grammar and style checking with LanguageTool后,其余配置项才会变为可用。对应的设置键为languageToolEnabled,默认值为false。

Server URL(服务器地址)

  • 本地自托管服务器可填如http://localhost:8081(这也是 UI 中的默认占位提示http://localhost:8010与代码默认值http://localhost:8081之外常见的本地端口)。
  • 无需手动拼写完整路径:QOwnNotes 会自动在服务器地址后附加/v2/check端点。源码中 buildCheckUrl() 会先去除 URL 末尾多余的/,再检查路径是否已以/v2/check结尾,未结尾则自动追加。因此你只需要填服务根地址。
  • 该地址保存在设置键languageToolServerUrl中,默认http://localhost:8081。

Language(语言)

  • 下拉框提供Auto-detect(自动检测,对应语言代码auto)作为默认选项,这是设置键languageToolLanguage的默认值。
  • 从源码 initialize() 看,下拉框还预置了en-US(English (US))、en-GB(English (GB))、de-DE(German)、fr(French)、es(Spanish)、it(Italian)、nl(Dutch)。
  • 下拉框是可编辑的(editable),你可以直接输入任意 LanguageTool 支持的语言代码(如zh、pt-BR)。读取设置时,程序会先在预置项中按数据值查找,找不到则按文本匹配,仍找不到就把输入文本作为自定义语言代码写入(见 readSettings())。

Username 与 API key(认证)

这两个字段用于需要认证的服务:

  • 对于LanguageTool Premium或云端服务,将Server URL设置为https://api.languagetoolplus.com,并填入账号用户名与 API key。Username 占位提示为"可选的 Premium 账号用户名(例如你的邮箱地址)"。
  • 两个字段会同时随请求发送;如果使用无需凭据的本地或免费服务器,请保持两者为空。
  • API key 不会明文存储:写入设置时通过CryptoService::instance()->encryptToString(...)加密(见 storeSettings()),读取时解密。更细致的是,LanguageToolChecker::readSettings() 只在 LanguageTool 实际启用时才从系统钥匙串(Linux 上的 libsecret)解密 API key,避免无关用户被无谓地触发钥匙串访问。

Check delay(检查延迟)

控制停止输入后等待多久再向服务发起请求(即防抖时间):

  • 对应设置键languageToolCheckDelay,默认1500 ms;
  • UI 中(languagetoolsettingswidget.ui)的范围为500–5000 ms,步进 100 ms,后缀显示ms;
  • 值越小响应越即时,但请求越频繁;自建本地服务器延迟低可适当调小,远端服务建议保留较大值。

Categories(检查类别)

通过五个复选框选择要检查的类别。源码中它们与 LanguageTool API 的类别 ID 存在如下映射(见 languageToolEnabledCategoriesFromUi()):

UI 复选框映射的 LanguageTool 类别 ID默认启用
SpellingTYPOS是
GrammarGRAMMAR是
StyleSTYLE+REDUNDANCY是
PunctuationPUNCTUATION是
TypographyTYPOGRAPHY是

设置键languageToolEnabledCategories保存启用类别列表,默认值为TYPOS, GRAMMAR, STYLE, REDUNDANCY, PUNCTUATION, TYPOGRAPHY。启用类别会随请求通过enabledCategories参数发送;同时,代码会自动把未勾选的类别放进disabledCategories一起提交(见 LanguageToolChecker::readSettings()),确保服务端不返回这些类别的命中。

Test Connection(测试连接)

填写完上述配置后,点击Test Connection可验证服务是否可达。源码中(on_languageToolTestConnectionButton_clicked())会使用当前表单里的 URL、语言、用户名、API key 与类别构造一个请求,发送一段特意包含语法错误的测试文本"This are a test sentence.":

  • 请求成功则弹出LanguageTool connection successful.;
  • 失败则弹出LanguageTool connection failed: <错误信息>。

测试请求的超时为 5000 ms。

管理忽略项

设置面板底部提供两个重置按钮(languagetoolsettingswidget.cpp):

  • Reset ignored rules:清除所有被忽略的规则(对应设置键languageToolIgnoredRules);
  • Reset ignored words:清除所有被忽略的单词(对应设置键languageToolIgnoredWords)。

点击时会先弹出确认框,确认后清空并触发编辑器整篇重新高亮。日常使用中,忽略的规则与单词分别通过右键菜单中的Ignore this rule与Ignore word "..."累积(详见下文"编辑器内的交互")。

快速开关:Spelling 菜单

除了设置对话框,你还可以在Spelling菜单中直接切换Check grammar with LanguageTool勾选项。该菜单项在 src/mainwindow.ui 中紧随Check spelling之后定义,是一个checkable动作;勾选状态与设置键languageToolEnabled双向同步(见 src/mainwindow.cpp)。切换时会写入设置并通知编辑器刷新(on_actionCheck_grammar_with_LanguageTool_toggled())。

工作原理:源码级的检查流程

只检查可见的编辑器块

QOwnNotes 不会一次性把整篇文档发给 LanguageTool,而是只检查当前视口内可见的文本块。visibleBlocks() 通过编辑器视口的左上角与右下角坐标,用cursorForPosition()定位首个与末个可见文本块,然后遍历其间所有块。

跳过空行、标题与代码块

可见块还需通过 shouldCheckBlock() 的过滤:

  • 空行(trimmed().isEmpty())跳过;
  • Markdown 标题块(MarkdownHighlighter::HeadlineEnd状态)跳过;
  • 代码块(MarkdownHighlighter::CodeBlock及CodeCpp之后的状态)跳过。

这样可以避免对标题、围栏代码等不该被语法检查的内容发起请求。此外,检查结果落库时还会再次确认目标块不是代码块(见 storeMatches())。

防抖(debounce)

每次文本变化(textChanged)或滚动(valueChanged)都会触发 scheduleCheck(),但默认并不会立即发起请求,而是重启一个单次QTimer,等待_checkDelayMs(即上面配置的 Check delay)后才真正执行 recheckNow()。滚动时触发的检查同样受此防抖控制,避免服务被高频查询。

可见块的哈希缓存与合并请求

recheckNow()会对每个可见块计算 SHA-1 哈希,与缓存中的结果比对(缓存条目默认 TTL 300 秒、上限 500 个块,见 cleanupExpiredCache()):

  • 内容未变的块直接复用缓存结果;
  • 仅将内容变化的块拼接为一个合并文本(块之间以换行分隔),连同各块起始偏移量一起合并成一次请求发送,从而大幅减少请求次数;
  • 每次请求记录发起时的文档修订哈希,响应返回时若文档内容已变化(documentRevisionHash不一致),则丢弃过期结果,避免把旧结果画在新文本上(见 handleRequestFinished())。

与服务器的 HTTP 交互

LanguageToolClient::checkText() 以application/x-www-form-urlencoded格式 POST 到服务器地址 + /v2/check,携带参数包括:

  • text:合并后的待检查文本;
  • language:语言代码(为空时发送auto);
  • username/apiKey:仅在非空时附带;
  • enabledCategories/disabledCategories:以逗号分隔的类别 ID 列表。

每个请求配有独立超时定时器,默认超时 5000 ms(设置键languageToolTimeout),超时即中止请求。响应按 LanguageTool 的 JSON 格式解析(parseMatches()):读取matches数组,提取每条命中的offset、length、message、shortMessage、context.text、rule.id、rule.url、rule.category.id以及replacements[].value。特别地,名为WHITESPACE_RULE的重复空格警告会被主动忽略——因为 Markdown 列表的缩进本来就依赖连续空格。

内联渲染与类别配色

检查结果通过blockMatchesUpdated信号通知编辑器,触发高亮器对相关块重绘(见 src/widgets/qownnotesmarkdowntextedit.cpp)。高亮逻辑位于 highlightLanguageTool(),不同类别使用不同下划线颜色:

类别颜色
TYPOS(拼写)红色
GRAMMAR(语法)#2a6fdb蓝
STYLE/REDUNDANCY(风格)#c99500橙
PUNCTUATION(标点)#1c8f47绿
TYPOGRAPHY(排版)#7a3db8紫

下划线采用SpellCheckUnderline样式,且逐字符应用下划线格式以保留每个字符原有的前景色(如标题、粗体、链接的着色),避免整段覆盖导致文字颜色错误(该修复对应 issue #3496,见 setLanguageToolUnderline())。下划线的 tooltip 会带上规则的完整说明文本。

与拼写检查的协作

LanguageTool 的TYPOS类别命中会与 Sonnet 拼写检查器协作:当 Sonnet 处于激活状态且 LanguageTool 的拼写命中覆盖了某段文本时,以 LanguageTool 的建议为准,同时避免双重下划线(见 highlightLanguageTool())。

编辑器内的交互:右键菜单

当光标悬停在带下划线的命中上时,右键菜单会插入一段 LanguageTool 专属区域(addLanguageToolMenuSection()),包含:

  • 标题行:显示命中类别(如GRAMMAR)与shortMessage/message摘要,禁用不可点击;
  • 替换建议:从replacements中取前 8 条作为菜单项,点击即替换;
  • More info...:若该规则提供了rule.url,则通过系统浏览器打开规则文档;
  • Ignore this rule:调用checker->ignoreRule(ruleId),将该规则加入忽略集合并持久化,随后整篇重新高亮;
  • Ignore word "...":对拼写类命中额外提供,调用ignoreWord()——它不仅被 LanguageTool 忽略,还会同步告知 Sonnet 拼写检查器把该词加入用户词典(见 ignoreWord())。

应用替换时,applyLanguageToolReplacement() 会先调用MainWindow::allowNoteEditing()解除可能的只读模式,确保替换后的内容能够被正常保存。

注意事项

  • LanguageTool 支持是可选的,依赖构建时是否启用LANGUAGETOOL_ENABLED;
  • 若服务不可达,QOwnNotes 会在状态栏显示一次LanguageTool is unavailable: <错误信息>警告(见 handleRequestError()),并停止检查直到服务恢复可用(_warningShown标志保证只提示一次,避免反复弹警告);
  • 你可以搭配本地自托管的 LanguageTool 服务器,或任何兼容/v2/check端点的 LanguageTool 服务使用;
  • 该功能与内置拼写检查互补:拼写检查依赖 Hunspell 词典,而 LanguageTool 额外覆盖语法与风格层面,二者可同时开启。

相关文档

  • 拼写检查(Spellchecking):Hunspell 词典的安装与配置,以及 Sonnet 后端的说明;
  • 概念(Concept):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
点击查看免费下载
上一篇:CTF 流量取证:HTTPS 加密流量的分析与服务端私钥解密实战(ctf-wiki)
下一篇:Apache Pulsar Namespace 管理实战指南:pulsar-admin、REST API 与 Java API 完整操作手册

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

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询