Monaco:前世今生与在 Theia 中的角色
继 Lumino 和 InversifyJS 之后,这一块就轮到编辑器内核 Monaco。
这篇先不急着进 API,而是想搞清楚三件事:Monaco 到底是什么,它和 VS Code/其他编辑器有什么关系,以及它在 Theia 里具体扮演什么角色。
Monaco 是什么?和 VS Code 什么关系?
Monaco 是 VS Code 用的那套编辑器内核,被抽出来做成了一个可以单独在浏览器里使用的库。
- 它提供的是代码编辑相关的所有“底层能力”:
- 文本模型(
ITextModel)、视图(Editor)、多光标、箭头键行为等; - 语法高亮、代码折叠、代码格式化、代码补全、跳转定义等语言特性接口;
- 差异视图(diff editor)、只读视图、只显示某些行的装饰器等。
- 文本模型(
- 它不关心:
- 应用的菜单、布局(这是 Lumino/Theia 的事情);
- 工程结构、终端、Git 集成(这些是 IDE 壳做的事)。
VS Code 本身是一个“大壳 + 多种服务”的集合:
- Monaco 负责“编辑器中间那块写代码的区域”;
- 其他部分(侧边栏、面板、命令面板、调试视图等)是用别的技术栈/基础设施堆出来的。
Theia 做的事情,有点类似于:
用 Lumino + InversifyJS 搭一个“VS Code 风格的壳”,然后把 Monaco 这块编辑器内核嵌进去。
为什么不是 CodeMirror / Ace,而是 Monaco?
在 Web 编辑器内核里,常见选项还有 CodeMirror、Ace 等。
Monaco 相比它们,有几个 Theia 这种 IDE 场景特别在意的优势:
语言服务友好
- Monaco 的
monaco.languages模块为各种语言特性(补全、诊断、重命名、跳转定义等)提供了统一接口; - 和 LSP(Language Server Protocol)非常契合,VS Code 本身就是 LSP 的头号用户。
- Monaco 的
大文件与复杂编辑场景经验丰富
- VS Code 已经在各种“打开几万行文件”“多光标编辑”等场景里被折腾过很多轮;
- Monaco 继承了这些经验,对滚动性能、高亮延迟加载、tokenization 做了不少优化。
和 VS Code 生态的自然对接
- Theia 希望尽量靠近 VS Code 的用户体验和扩展生态(包括 VS Code 插件兼容);
- 用 Monaco 能在编辑体验上减少差异,比如快捷键、选择行为、折叠/小地图等。
简单说:
如果目标是做“类 VS Code 的 Web IDE”,选 Monaco 几乎是顺理成章。
在 Theia 里,Monaco 和 Lumino / InversifyJS 是怎么分工的?
可以把现在你看过的三块东西做个简单对照:
InversifyJS:负责依赖注入 + 扩展点
- 谁是服务,谁依赖谁,扩展怎么挂进系统,生命周期怎么管。
Lumino:负责桌面感窗口/布局
- DockPanel / SplitPanel / TabBar 等,把各种视图拼成一个 IDE 壳。
Monaco:负责代码编辑区本身
- 文本模型、光标行为、语言特性、装饰器、差异视图等。
在 Theia 的前端结构里,大概是这样的层次:
- 最外层是 ApplicationShell(Lumino DockPanel 驱动的布局)。
- Shell 的某个区域里挂着 Editor Widget(Theia 自己的
MonacoEditor/TextEditor抽象)。 - 每个 Editor Widget 里面再嵌着一个真正的 Monaco Editor 实例(
monaco.editor.IStandaloneCodeEditor等)。
也就是说,Monaco 从来不直接决定“窗口在哪里、怎么分屏”,它只负责“一块矩形区域里的编辑体验”。
Theia 具体是怎么“装 Monaco 进壳”的?
这部分在专门讲 MonacoInit 的文章里会展开,这里先给一个大致轮廓:
在前端入口(你已经看到的
index.js)里,有一行:1const { MonacoInit } = require('@theia/monaco/lib/browser/monaco-init'); 2// ... 3MonacoInit.init(container);MonacoInit.init(container)这一步主要做的事情包括:- 配置 Monaco 环境(比如 worker 加载方式、语言注册等);
- 把与 Monaco 相关的服务/工厂注册到 DI 容器中(通过 InversifyJS 绑定);
- 为后续
MonacoEditor的创建提供基础设施。
之后,在 Theia 的编辑器模块里,会有类似:
- 把某个
MonacoEditor绑定到容器; - 暴露
EditorManager/TextEditor抽象,供其它扩展使用。
- 把某个
最终效果是:
Theia 里的其它代码几乎不用直接碰 Monaco 的底层 API(除非你愿意),只需要通过 Theia 抽象(如 TextEditor、EditorManager)来操作编辑器。