一个内核,两张脸:先把终端编辑器跑起来

一个内核,两张脸:先把终端编辑器跑起来

文本能改、能撤销之后,接下来要把它变成一个能操作的程序。

砚最先接的是 tcell 终端前端。早期闭环很直接:打开文件、移动光标、输入、保存,再退出。后来 macOS 窗口接进来时,这些能力仍然来自同一个内核。

这件事成立,靠的是一开始把"谁能改状态"说清楚。

状态交给一个所有者

core/app.Editor 的可变状态归前端事件循环所在的 goroutine 所有。输入分发、命令执行、切换标签和构建画面都在这里完成。

搜索和目录遍历可以放到后台,但后台不随手去改当前标签、选区或侧边栏。它读快照,算完通过 Post 把结果交回来,由所有者处理。

text 复制代码
键盘 / 鼠标
    ↓
前端翻译成语义输入
    ↓
Editor:修改状态、执行命令、构建 Frame
    ├─ 交出快照 → 后台计算 → Post → 回到 Editor
    ↓
前端绘制

这么做没有消灭并发问题。后面照样遇到了"旧工作区任务返回到新工作区"的错误。但至少排查时,不必先猜十几个 goroutine 谁正在修改同一份状态。

Frame 是两边的约定

内核给前端一份 Frame,描述要显示哪些内容。终端和窗口用自己的方式画出来,键盘鼠标再翻译回语义动作。

接入 GUI 后,Frame 变得更完整:侧边栏、多栏与标签、代码窗格、状态栏、浮层、查找框。这些信息仍然不要求内核知道某个具体 UI 组件。

这种分层也允许前端不同。窗口版能有富文本预览和内嵌终端;终端版可以只提示图片路径,把 shell 接管和恢复作为自己的工作流。共用内核,并不等于每个界面必须画出完全相同的东西。

命令是功能的共同入口

保存这件事,可以从快捷键、菜单或命令面板触发。三个入口最好调用同一条命令,否则很容易出现某个入口忘记处理冲突或状态提示。

砚用了命令注册表,命令 ID 尽量沿用熟悉的 VS Code 名称。键位解析支持组合键、连按和焦点上下文。资源管理器里的方向键与编辑器里的方向键,因此可以走不同处理。

终端还多了一层现实限制:Terminal.app 不能按窗口程序的想法接收所有 Cmd 组合。所以基础键位使用 Ctrl 和 Ctrl+K 连按回退;支持相应键盘协议的终端,再提供更多组合。

这件事让我意识到,快捷键表必须根据实际输入通道来设计。把熟悉的符号写在说明书里,并不能让终端把它送过来。

最早的终端功能:把 shell 还给用户

早期没有直接实现底部终端面板,而是提供 shell 往返。

按 Ctrl+K T,编辑器让出终端,在项目目录启动交互式 shell。运行命令后输入 exit 或按 Ctrl+D,再回到编辑器,文件、光标和撤销历史继续保留。返回时刷新工作区相关信息。

它很适合那个阶段:编辑器负责编辑,运行项目仍然用现成的 shell。后来窗口版有了内嵌终端,终端前端也没有因此失去这条路径。

架构边界要能被测试发现

"内核独立"如果只写在文档里,添加功能时很容易顺手导入一个 UI 包。

砚把几条规则做进了架构测试:core 不能依赖 tcell、MyGo、UI 或平台实现;两个前端互不导入;应用代码的依赖边界还检查 cgo 情况。平台剪贴板、shell、回收站等能力通过接口交给宿主实现。

这些测试没有证明整个架构永远正确,却能让最容易发生的依赖倒置及时报错。

终端骨架跑起来后,这个项目终于有了能反复使用的最小闭环。下一步要处理的,则是一个从第一天就不能拖到最后的问题:中文。


相关推荐
Patrick_Wilson3 小时前
Go 为什么需要 defer?从函数怎么收尾说起
后端·go·编程语言
Patrick_Wilson4 小时前
从文本输出理解 Go 方法与接口
后端·go·编程语言
Patrick_Wilson9 小时前
读懂一个 Go JSON 处理函数:从输入数据追踪结构体、校验与错误返回
go·json·编程语言
Patrick_Wilson9 小时前
第一次读 Go 项目:从两个源码文件弄清 package、import 和 go.mod
后端·go·编程语言
Patrick_Wilson9 小时前
执行 go build . 后,Go 怎样找到代码并生成程序
后端·go·编程语言
Patrick_Wilson9 小时前
Go 变量不赋值时,为什么也有值
后端·go·编程语言
Patrick_Wilson9 小时前
读 Go 项目里的命令:run、build、test 分别做了什么
后端·go·编程语言
vipxieliang14 小时前
Go GC 性能优化实践:从 GOGC、GOMEMLIMIT 到分配热点定位
go
孔令飞14 小时前
很多人想学AI Infra,今天推荐一个 AI Agent / AI Infra 学习路线
云原生·go·ai编程
我不会起名字3221 天前
Redis 从 0 到 1(十七):订单 30 分钟不支付自动关闭——ZSet 延迟队列 Go 实现
redis·go·延迟队列·zset·订单