monaco-editor指南-断点管理
如果你的编辑器涉及调试,那么断点功能是必不可少的了。monaco自身没有单独用于断点的API,而是使用 deltaDecorations 进行统一的行装饰管理。
在monaco playground中官方做了简单的示例,但这个还不足以称之为断点,只是长得看上去像断点,并没有一般编辑器中断点增删功能,所以我们要为其增加断点功能。
monaco内部维护着所有行装饰对象,它们都通过内部id进行标示,可以使用editorInstance.getModel().getAllDecorations()
获取编辑器内所有装饰对象。行装饰的添加与删除都是通过deltaDecorations API完成的,它的第一个参数接收旧的装饰id,第二个参数是新的装饰对象。如果新的装饰对象的id不在旧的id列表中,那么将删除缺失的id对应的装饰,反之则增加一个装饰。通过它我们就能实现断点点击的增删功能:
1function breakPointService(ins: editor.IStandaloneCodeEditor) {
2 ins.onMouseDown(e => {
3 const { target } = e;
4 if (target.type === editor.MouseTargetType.GUTTER_GLYPH_MARGIN) {
5 const old = ins
6 .getModel()
7 .getAllDecorations()
8 .filter(x => x.options.glyphMarginClassName)
9 .filter(
10 x =>
11 x.options.glyphMarginClassName.indexOf(
12 css.breakPoint,
13 ) !== -1,
14 );
15 const oldId = old.map(x => x.id);
16 const existIndex = old.findIndex(
17 x => x.range.startLineNumber === target.position.lineNumber,
18 );
19
20 if (existIndex === -1) {
21 old.push({
22 id: "",
23 ownerId: 0,
24 range: new Range(
25 target.position.lineNumber,
26 target.position.column,
27 target.position.lineNumber,
28 target.position.column,
29 ),
30 options: {
31 isWholeLine: true,
32 glyphMarginClassName: css.breakPoint,
33 stickiness:
34 editor.TrackedRangeStickiness
35 .NeverGrowsWhenTypingAtEdges,
36 },
37 });
38 } else {
39 old.splice(existIndex, 1);
40 }
41
42 ins.deltaDecorations(oldId, old);
43 }
44 });
45}
这里我们使用函数breakPointService来为某一个编辑器实例添加断点服务。在编辑器接到onMouseDown事件后,判断点击来源是否为GUTTER_GLYPH_MARGIN,表示行号左边的区域,要显示这个区域,需要在editor.create时添加配置glyphMargin: true
。
接着获取当前的断点装饰数据,通过options.glyphMarginClassName中有没有css.breakPoint来判断这是不是一个断点装饰对象。接着判断当前行有没有断点,如果没有,则新增一个行装饰,反之则在旧id列表中将该id删除,最后调用deltaDecorations即可。
现在我们已经实现了断点管理了,点击行号左侧区域可以增删断点。这里有一个额外配置:stickiness: editor.TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges
,表示换行时行装饰不随换行而扩展(除非该行是空白行)。通常我们添加断点肯定是只想对这一行添加,如果在该行按下回车,那么断点应该只留在上一行。默认情况下按下回车,行装饰会扩展至下一行,所以需要添加该配置确保断点的正确显示。
不幸的是,monaco只支持显示唯一的glyph,也就是断点哪一列只支持显示单个元素。如果你需要在glyph列显示多个状态图标,例如断点、书签或其他按钮,可以给glyphMarginClassName挂上多个class来实现。
现在我们已经实现了断点管理了,接着可以进行进一步开发了。