vscode 中最核心和最重要的内容是它的代码编辑器:monaco editor,monaco editor 不仅是 vscode 的一部分,并且可以作为独立的 web 组件供大家免费使用。 关于 monaco editor 的集成详细文档,大家可以查阅微软的官方文档。
var newModel = monaco.editor.createModel(data, 'some id here!');
var editor = monaco.editor.create(document.getElementById('editor'), {
model: newModel,
});
monaco editor 的创建过程如上所示,可以看出 monaco editor 有两个核心的对象: model 和 editor。model 部分是存在代码字符串数据的对象,它的内部结构非常复杂,我们会在后面的文章中深入。editor 就是整个代码编辑器。
为了一步一步探索编辑器内部的神秘面纱,我们先从它的 DOM 结构开始。
最外层结构:
- 容器元素
- 编辑器主体
- overflow-guard,编辑器的主要区域
- overflowingContentWidgets,与右键菜单相关的内容
- context-view, 与右键菜单相关的内容
- 编辑器主体
对于创建 monaco editor 的时候,有一个隐性的约束,容器的高度必须是确定的,编辑器本事会支持部分的宽度自适应。在创建好 Editor 的时候,editor 会在容器元素上添加两个属性:
- data-keybinding-context="1", 标示这个编辑器绑定快捷键的方式
- data-mode-id="", 标示编辑器中的 model id
overflow-guard 编辑器的主要区域:
- margin: 左侧的代码行号区区,上面列有代码行号,折叠代码图标以及加断点标记。
- editor-scrollable: 主要代码工作区域
- scroll-decoration: 滚动阴影装饰窄条,当开始滚动时出现在顶部
- textarea:鼠标输入框
- monaco-editor-background textAreaCover line-numbers (主要起装饰作用,具体作用暂时未知,以后补充)
- overlayWidgets: 按 ctrl + F 弹出来的查找替换搜索框在这里面
- minimap: 右侧的代码小地图
textarea 元素
textarea 元素是一种可以自由控制光标位置显示的技巧。
代码之所以在编辑器中会显示出花花绿绿的各种语法高亮,那是因为它们本身是由一个一个span组成的,并且不是 input 输入框。当我们在编辑器中输入代码时,需要一个光标显示在对应的位置,这个时候就可以控制一个 textarea 元素,它的宽度是 1px,高度对应字体高度,将它的位置设置到当前的位置,并且设置为 focus 状态了,这个时候界面上就会在对应位置上出现一闪一闪的光标。
这是一个普遍的技巧,我们常见的 select 组件中经常会使用到,另外一个常见的场景是在交互式 canvas 中需要用户输入的地方。
overlayWidgets 元素
我们通过按 ctrl + f 键,会在代码编辑器中弹出一个有查找替换框,这个查找替换框就是在 overlayWidgets 里。当然,overlayWidgets 里还有别的 widget,限于当前的认识,无法确切知道是具体是做什么的,但随着我们学习的深入,这些会在以后慢慢揭秘。
minimap 代码小地图
minimap 的功能相对独立,DOM 结构非常简单:
- minimap-shadow-hidden, 主要起UI装饰装饰作用
- canvas:绘制整个代码的缩略图
- canvas:minimap-decorations-layer, 绘制语法错误的行
- minimap-slider:minimap 上的半透明罩层,表示当前代码在整个文档中的滚动位置
minimap 是一个很有意思的话题,它的实现特别精巧。这里有一个细节需要注意一下,在UI 上绘制小物体时,通常会选择按大尺寸绘制,然后成比例设置为一个小的 css 来控制最终的显示。
代码缩略图是如何绘制的
代码的缩略图如下:
vscode 对于它的实现给了一个非常有意思的答案,具体代码是在 /src/vs/editor/browser/viewParts/minimap 目录下。
microsoft/vscode
这里简单快速地说一下原理,在 vscode 中将 ascii 码 32 到 126 之间的字符,按照两种尺寸(宽1px高2px,和宽2px高4px)提前准备到一个 map 里。具体代码如下:
将这个 map 里的数据转换成图片显示出来就是如下的样子:
在绘制代码的时候,是一个字符一个字符的绘制,如果字符是基础字符集,则从这个map里使用它的像素信息,再对应上字体在文本中的高亮颜色。如果字符是汉字之类,则绘制一个方块。最终绘制的效果如下,可以隐约能看出字体的轮廓:
总结
关于 monaco editor 的 DOM 结构,我们做了一个简单的概览,有了一个大体的了解。关于 Margin 部分,和代码工作区的部分,在后续单独的章节里再展开。
比如虚拟列表技巧的普遍使用,比如一个文档有 1w 行,真实在 DOM 里只会有 50 个左右。不同于我们经常用 virtual-list,vscode 代码编辑器可以下拉到超出文档内容很多的底部。
今天对代码的部分讨论的很少,从 DOM 结构开始切入,对于monaco editor 有一个整体的了解,先知道 What,再去深入 How 的问题。从本质上来说,文档/UT 都是属于代码,代码是一种表象,更重要的是代码背后的设计思路。