Bootstrap

Monaco Editor系列(四)版本对比、自定义右键菜单、光标滚动

前言:亲爱的小伙伴们,又见面了!上一篇文章我们一起学习了 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);

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;