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