鸿蒙 PC Markdown 编辑器错误处理:让失败可恢复而不是只弹提示

📅 2026/7/19 18:24:40 👁️ 阅读次数 📝 编程学习
鸿蒙 PC Markdown 编辑器错误处理:让失败可恢复而不是只弹提示

鸿蒙 PC Markdown 编辑器错误处理:让失败可恢复而不是只弹提示

编辑器错误处理的第一目标不是把异常文字显示出来,而是保证失败后用户仍有下一步。打开失败不能覆盖当前缓冲区,保存失败不能清 dirty,选择器取消不是错误,正则非法不能破坏正文,ArkWeb调用失败要保留原生快照,恢复记录损坏不能形成启动崩溃循环。

本文基于 OhMarkdown,梳理状态栏、对话框、回退值、保存恢复和操作互斥组成的可恢复错误模型。代码位于 https://gitcode.com/VON-/codex_md_oh

错误先按后果分级

可忽略取消:用户关闭文件/保存选择器,恢复 Ready或 Modified,不显示失败。

可重试操作:目录读取、搜索、导出、打印失败,保留当前状态并给状态栏信息。

需要决策冲突:外部文件变化、Mixed换行、未保存关闭、保存中断,使用明确对话框和按钮。

数据安全故障:短写、旧版本恢复失败、恢复快照写入失败,保留缓冲区或备份并标记 P0/P1。

按后果分类比把所有 catch弹对话框更适合高频桌面工具。

operationStatus 是非阻断反馈

页面持有:

@StateprivateoperationStatus:string='Ready';@StateprivateoperationInProgress=false;

状态栏始终显示 operationStatus并省略超长文本。常见状态包括 Ready、Opened、Modified、Saving、Saved、Loading folder和具体失败。

状态文案不能成为业务事实来源。关闭判断读取 dirty,保存完成读取 revision,不能因为字符串等于 Saved就删除会话。状态栏是输出,不是状态机。

选择器取消不是异常

constopenedDocument=awaitpickMarkdownDocument(context);if(!openedDocument){this.operationStatus=this.hasUnsavedChanges()?'Modified':'Ready';return;}

用户取消是正常分支。保留当前标签、焦点和 dirty,不显示红色失败。保存 URI为空和格式对话框取消也使用同样语义。

如果保存并关闭来自外层意图,finally检查 dirty仍为 true,因此不关闭。取消会正确向外传播为“事务未提交”。

打开失败不应用半状态

this.operationInProgress=true;this.operationStatus='Opening...';try{awaitthis.applyOpenedDocument(awaitreadUtf8Document(documentUri));}catch(error){this.operationStatus=`Open failed:${errorinstanceofError?error.message:String(error)}`;}finally{this.operationInProgress=false;}

读取完整成功后才 apply。文件过大、非法 UTF-8、短读或权限错误都停在旧会话。不要先清编辑器显示 Loading再读,否则失败时用户看到空白且误以为内容丢失。

页面可以显示进行中状态,但正文保持可见。操作互斥阻止失败窗口内切换目标。

保存失败保持 dirty

保存函数只有完整写入、truncate、fsync且 revision未变化时设置documentDirty=false。catch不调用 markSaved,也不清恢复草稿。

}catch(error){constfailureMessage=errorinstanceofError?error.message:String(error);this.operationStatus=`Save failed:${failureMessage}`;}

若已创建 pending备份,catch先尝试恢复外部旧文件;成功显示 previous file restored,失败显示 backup retained。无论哪种,当前新正文仍在标签中。

错误信息告诉用户磁盘结果,但不能泄露完整敏感 URI。未来可把底层错误映射为“权限已撤销”“空间不足”“文件被修改”等可操作类别。

外部修改是冲突,不是普通失败

保存前重新读取磁盘,与 persisted正文和格式比较。不一致抛出:

The file changed on disk. Reopen it or use Save As to avoid overwriting external changes.

此时不能自动重试,因为重试只会重复冲突;用户需要 Reopen、Compare或 Save As。当前通过状态栏提示,后续应提供操作按钮。冲突处理不应默认“最后写入者获胜”。

对话框失败选择保留

脏标签关闭对话框 Promise失败:

.catch(()=>{this.operationStatus='Unable to show confirmation';});

标签不关闭。Mixed格式对话框失败向上抛,保存catch处理,dirty保留。只要无法取得明确用户决策,就选择不破坏数据。

对话框按钮索引之外的返回也视为取消或保留记录,不猜用户意图。

恢复记录损坏不能阻断启动

try{constrecord=JSON.parse(awaitfileIo.readText(path,{encoding:'utf-8'}))asRecoveryRecord;returnisRecoveryRecordValid(record)?record:undefined;}catch(_){returnundefined;}

空、过大、非法 JSON和字段错误返回 undefined。应用继续打开。可在非敏感日志记录“恢复记录无效”,但不能因为辅助文件损坏形成每次启动崩溃。

有效记录显示 Recover/Discard;对话框异常时 catch把状态设为 Recovery unavailable,记录仍在,下一次可重试。

恢复队列失败保留最新记录

}catch(error){if(this.pendingRecoveryRecord===undefined){this.pendingRecoveryRecord=record;}throwerror;}

写入失败时若没有更晚快照,把当前记录放回 pending;若已有更新版本,不用旧记录覆盖。外层状态显示 Recovery failed。不会无限忙重试,下一次编辑快照再触发。

恢复失败不能阻止用户继续编辑,但属于质量红线,应在内部试用记录。

ArkWeb失败使用旧正文

letcontent=this.documentContent;if(this.editorReady){try{constresult=awaitthis.editorController.runJavaScript('window.OhMarkdownEditor?.getDocument() ?? ""');content=this.decodeJavaScriptString(result);}catch(_){}}

捕获失败保留原生最近快照,不写空。runEditorScript失败将 editorReady=false,页面再次 ready后重放文档、模式、主题和同步设置。

这种回退可能不是最后一个按键版本,所以恢复快照和保存命令仍应尽量在 Web可用时完成。降级路径的目标是“不扩大损失”,不是假装没有错误。

搜索与正则错误

Web收集匹配用 try/catch,非法正则返回空数组;原生显示 No matches或 Replace failed。当前无法区分语法错误与零匹配,是体验边界,但正文不会变化。

Replace All先收集完整 changes,再一次 dispatch。异常发生在 dispatch前不会部分替换;单 transaction还可一次撤销。未来应返回结构化错误和表达式位置。

导出与打印失败

HTML导出先选择 URI,再请求 Web字符串、解码、检查非空后写入。任何错误状态为 HTML export failed,当前 Markdown会话不变。打印先要求preparePrint() === true,大文档直接拒绝,不创建不完整适配器。

导出目标可能已经创建空文件后失败,后续应考虑删除或标记;不能为了清理目标影响源文档。

操作互斥避免竞态错误

文件、目录、导出和打印入口检查operationInProgress。finally始终释放。没有 finally会让一次异常永久禁用工作台;提前释放又可能让异步返回写错活动会话。

当前是全局互斥,简单可靠。后续并行后台保存需要显式 OperationContext,而不是删除锁。

错误后的 UI 要可继续操作

按钮 enabled绑定 operationInProgress和 largeDocumentMode。操作结束即恢复;状态栏错误不覆盖正文;侧边栏仍可切换;脏标签仍能另存或复制。阻断对话框只用于需要用户决策的安全边界。

下图为真实文档打开后的应用界面。状态栏承担操作结果,正文和文件树保持可用。

错误截图应使用专用测试文件,避免暴露用户路径。行为报告记录失败前后正文哈希和 dirty,而不是只截错误文字。

错误信息与本地化

当前部分状态为英文硬编码,资源化尚未完全。做大前应建立错误码到本地化文案和操作的映射,底层 Error保留诊断,UI显示中文可执行建议。

不要把原始系统堆栈直接给用户,也不要完全吞掉。开发日志记录错误类别、API和耗时,生产 UI显示安全文案。

P0 红线

覆盖或丢失用户文件、恢复失败且无备份、保存状态错误清 dirty、跨标签写错正文都为 P0,应停止扩展。普通搜索失败、预览样式和目录截断可按 P1/P2处理。

内部试用每日记录崩溃、数据丢失和 P0/P1,不收正文。错误分类必须进入退出评审。

当前边界

错误仍以字符串为主,没有结构化码;非法正则与零匹配不区分;冲突缺 Compare/Save As快捷动作;导出失败目标清理未完善;恢复写失败没有指数退避;部分文案未本地化。

结语

可恢复错误处理的判断标准是失败后数据和下一步。OhMarkdown把取消当正常分支,读取成功后才应用,保存成功后才清 dirty,冲突不覆盖,记录损坏不阻断启动,Bridge失败保留旧正文,所有异步用 finally释放互斥。

鸿蒙 PC编辑器不可能消除所有故障,但可以保证故障不会自动变成数据丢失,也不会把用户困在只能重启的界面。