Z

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来实现。

现在我们已经实现了断点管理了,接着可以进行进一步开发了。