- 桌面应用
【免费下载链接】QOwnNotes
QOwnNotes is a plain-text file notepad and todo-list manager with Markdown support and Nextcloud / ownCloud integration.
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 | 默认启用 |
|---|---|---|
| Spelling | TYPOS | 是 |
| Grammar | GRAMMAR | 是 |
| Style | STYLE+REDUNDANCY | 是 |
| Punctuation | PUNCTUATION | 是 |
| Typography | TYPOGRAPHY | 是 |
设置键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.
相关推荐
QOwnNotes 集成 LanguageTool:在 Markdown 笔记编辑器中实现语法与风格检查
QOwnNotes 集成 LanguageTool:在 Markdown 笔记编辑器中实现语法与风格检查 QOwnNotes 除了传统的拼写检查外,还可以接入本
桌面应用QOwnNotes 接入 Harper:在笔记编辑器中启用离线语法与风格检查
QOwnNotes 接入 Harper:在笔记编辑器中启用离线语法与风格检查 导读 Harper 是 QOwnNotes 编辑器内置的可选 离线语法与风格检查
桌面应用QOwnNotes 集成 LanguageTool 实现语法与风格检查:配置、工作原理与源码解析
QOwnNotes 集成 LanguageTool 实现语法与风格检查:配置、工作原理与源码解析 导读 QOwnNotes 是一个基于纯文本文件的 Markdo
桌面应用
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考