前言:亲爱的小伙伴们,又见面了!上一篇文章我们一起学习了 Monaco Editor 的几个功能,设置内容、多文件编辑、自定义主题;下面让我们继续Monaco Editor的旅程吧!
前情提要:
上一篇文章我介绍了Monaco Editor的几个功能,涉及到的方法分别是:
🧜🏼♀️ 获取 model editor.getModel()
🧜🏼♀️ 获取内容 editor.getValue()
🧜🏼♀️ 设置内容editor.setValue()
🧜🏼♀️ 监听内容变化 editor.onDidChangeContent()
🧜🏼♀️ 创建 model monaco.editor.createModel(内容, 语言)
🧜🏼♀️ 设置 model editor.setModel(newModel)
🧜🏼♀️ 获取 model 的状态 editor.saveViewState()
🧜🏼♀️ 设置 model 的状态 editor.restoreViewState(state)
🧜🏼♀️ 编辑器获取焦点 editor.focus()
🧜🏼♀️ 设置 model 的语言 editor.setModelLanguage(model, language)
🧜🏼♀️ 设置主题 monaco.editor.setTheme(themeName)
🧜🏼♀️ 定义主题 monaco.editor.defineTheme(主题名, 配置对象)
一、版本对比
playground 路由页面这几个都是版本对比的示例
在我的日常工作中,提交的代码需要提交 code review,然后让别的同事进行检查,不知道大家和我的工作流程一不一样呢?此时就用到 git 中的代码对比的功能,它会清晰的标出来新增代码、删除代码、修改代码。本地项目中给的也有示例哦
真清晰哇!咱们可以直接从右侧的代码区域看到执行的代码
从上面的代码中我们可以看到,创建版本对比编辑器需要三个步骤
① 创建旧代码的 model 和新代码的 model
② 创建 diffEditor
③ 给 diffEditor 配置两个 model
createDiffEditor()
方法的文档地址:
${本地项目根路径}/docs.html#functions/editor.createDiffEditor.html
参数一:编辑器的容器
参数二:编辑器配置对象 IStandaloneDiffEditorConstructionOptions
参数三:重写编辑器行为的服务 IEditorOverrideServices
一般来说也用不到
咱们的编辑器长啥样,关键在于配置对象 IStandaloneDiffEditorConstructionOptions
。定义在 node_modules/monaco-editor-core/monaco.d.ts 文件里,然后它继承了好多层🤣
救命啊家人们,怀疑人生了。。我发现源码里注释有一个错误。。
这个属性,注释的最后一句,默认为true
。但是经过我实践,其实默认值是 false
。。
这个属性是用来检测高对比度主题的,如果 theme
设置成高对比度,autoDetectHighContrast
设置成 true
的话,就会修正高对比度,还是应用这种白底红红绿绿的形式
如果设置成 false
,就会使用高对比度的样式,结果就是下面这样,不太好看
如果不设置的话,也是这样
我查阅了 IStandaloneDiffEditorConstructionOptions
接口里面的属性,写了下面的案例代码,其中有一些设置并没有效果🌚。总之大家参考一下下面的注释出来的含义吧。另外,除了下面注释出来的内容,其他的在创建 普通的编辑器的时候设置的属性,也是可以作为属性传给差异对比编辑器的
require(['vs/editor/editor.main'], function () {
var originalModel = monaco.editor.createModel(
"This line is removed on the right.\njust some text\nabcd\nefgh\nSome more text",
"text/plain"
);
var modifiedModel = monaco.editor.createModel(
"just some text\nabcz\nzzzzefgh\nSome more text.\nThis line is removed on the left.",
"text/plain"
);
var diffEditor = monaco.editor.createDiffEditor(
document.getElementById("container"),
{
theme: 'hc-light',
// 自定修正高对比度
autoDetectHighContrast: true,
// 编辑器为只读形式
readOnly: false,
// 是否允许拖动中间的中轴线改变左右编辑区域的大小
enableSplitViewResizing: true,
// 左侧默认占整体宽度的比例 数字 0-1 之间 默认是 0.5
splitViewDefaultRatio: 0.7,
// 是否并排渲染;如果是false,则会嵌套渲染
renderSideBySide: true,
// 如果 renderSideBySide 设置为true,宽度小于renderSideBySideInlineBreakpoint
// 的时候显示为内嵌模式 但是我测试的并不好使。。。
useInlineViewWhenSpaceIsLimited: true,
renderSideBySideInlineBreakpoint: 1000,
// 计算差异的周期 ms
maxComputationTime: 5000,
// 最大文件 MB
maxFileSize: 50,
// 忽略空格差异
ignoreTrimWhitespace: true,
// 新增和删除的行是否显示加号和减号
renderIndicators: false,
// 在每一行的左侧显示回滚的小图标,没看到哇?
renderMarginRevertIcon: true,
// 展示右上角的预览标尺
renderOverviewRuler: true,
// 原始文本是否可编辑,默认为false
originalEditable: true,
diffCodeLens: true,
}
);
diffEditor.setModel({
original: originalModel,
modified: modifiedModel,
});
});
二、自定义命令和菜单
在 Monaco 中,已经有很多现成的很好用的快捷键,例如 crtl+f 搜索代码。Monaco 其实还提供了接口,允许我们自定义命令。另外右键菜单里面的命令,和快捷键往往都是一对一的关系
右键出来的菜单项的专业术语叫做 Action
。
所以我们这一章就来学一学怎么自定义命令以及 Action
菜单项吧!
我们来实现一个简单的命令,alert 弹出一句话
(一)自定义命令
添加自定义命令的方法是 editor.addCommand()
为了方便大家点击链接跳转,以后我就直接贴官网的文档链接啦!
这个方法需要接受一个参数,类型为 ICommandDescriptor。接口也很简单,包含两个属性:
🥗 id 按键的值
🥗 run 事件处理函数
按键的值的定义在枚举值 KeyCode 上定义,在 node_modules/monaco-editor-core/monaco.d.ts 文件里
以 shift 为例子,我们在传递 id 的时候,可以直接传数字 4,但是这就要求我们每次都查阅 KeyCode 的定义,看一下按键对应的数字;更方便的是直接使用 monaco.KeyCode.Shift
。但是快捷键可能发生冲突,所以在这里可以使用组合按键,monaco.KeyMod.chord 方法
monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK)
表示组合按键 ctrl + k
。需要注意的是,monaco.KeyMod.chord()
传进来的第一个按键,必须是 KeyMod
上定义的几个静态常量
export class KeyMod {
static readonly CtrlCmd: number;
static readonly Shift: number;
static readonly Alt: number;
static readonly WinCtrl: number;
static chord(firstPart: number, secondPart: number): number;
}
第二个按键是枚举 KeyCode
中的按键
editor.addCommand(
monaco.KeyMod.chord(monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyK),
function () {
alert(`风遁:螺旋手里剑`);
}
);
按下 ctrl + k
(二)自定义菜单项 Action
playground 路由页面的下面两个选项是官方给出的增加命令和菜单项的示例
添加菜单项使用方法 editor.addAction
有两个方法可以添加 Action
,第一个是 monaco.editor.addEditorAction()
,给所有的编辑器实例添加 Action
;另一个是 editor.addAction()
,editor
实例调用 addAction()
,只给自己添加就完了
editor.addAction()
接收一个 IActionDescriptor类型 的对象作为参数
具体的含义我都写在代码的注释里啦
我们来创建一个 Action
让小迪实现艺术 💥
editor.addAction({
// 不能重复
id: 'bomb',
// 展示在菜单中的文本
label: 'Didara Bomb',
// 快捷键
keybindings: [
monaco.KeyMod.CtrlCmd | monaco.KeyCode.F10,
// 组合按键 需要 ctrl + d + m
monaco.KeyMod.chord(
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyD,
monaco.KeyMod.CtrlCmd | monaco.KeyCode.KeyM
)
],
// 前提条件 String
// 暂时只找到这一个例子,判断编辑器语言是不是 javascript
precondition: "editorLangId == 'javascript'",
// 绑定快捷键的规则
keybindingContext: "editorLangId == 'javascript'",
// 指定操作应显示在上下文菜单的哪个组中 navigation表示默认组
contextMenuGroupId: 'navigation',
// 操作在菜单中的显示顺序
contextMenuOrder: 1.5,
// 操作执行的方法
// @param editor ed.getPosition() 获取焦点坐标
run: function (ed) {
alert("艺术就是💣💣💣💣💥💥💥💥" + ed.getPosition());
}
});
然后我们右键查看已经有了新增加的菜单
点击就让小迪绽放
另外,上面我们还绑定了两种快捷键,一种是 ctrl + f10
,一种是 ctrl + d + m
📢📢📢 焦点需要在编辑器内按快捷键才有用哦
三、光标滚动
在 gitlab 中,点击某一行代码的行号的时候,会自动生成一个锚点,并且url中会显示锚点名称。通过这个链接分享出去,别人点击链接进来的时候,代码就会自动滚动到这个锚点处
这一章我们就来实现一下代码滚动的功能
如果要获取光标的位置,可以使用:
model.getPositionAt()
咱们本地项目的 playground 路由里面,有这个滚动功能的示例,并且里面也列举出了所有的关于滚动的方法
editor.revealLine // 编辑器滚动到某一行,只保证出现该行
editor.revealLineInCenter // 编辑器滚动到lineNumber行,并将改行滑动到编辑器垂直中心
editor.revealLines // 滚动到多行
editor.revealLinesInCenter // 滚动到多行中心
editor.revealLinesInCenterIfOutsideViewport // 滚动到多行中心 如果位于视口之外
editor.revealPosition // 滚动到固定的行,列
editor.revealPositionInCenter // 滚动到固定的行,列 中心
editor.revealPositionInCenterIfOutsideViewport // 滚动到固定的行,列 中心 如果位于视口之外
editor.revealRange // 滚动到一定范围,开始行,列,结束行列
editor.revealRangeInCenter // 滚动到一定范围,开始行,列,结束行列 中心点
editor.revealRangeInCenterIfOutsideViewport // 滚动到一定范围,开始行,列,结束行列 中心点 如果位于视口之外
使用的时候看哪一个比较适合酌情选择就好啦!
// 滚动到多少行多少列
// 第一个参数的类型是 IPosition
// IPosition {
// readonly lineNumber: number;
// readonly column: number;
// }
// 第二个参数是滚动的方式 0 表示 smooth 1 表示 Immediate
editor.revealPositionInCenter({ lineNumber: 44, column: 66 }, 0);