likes
comments
collection
share

聊聊monaco-editor的那些事儿~

作者站长头像
站长
· 阅读数 39

想必大家在平常的工作中,编辑器一定是打交道最多的工具,那你有没有想过自己开发一款编辑器呢?

聊聊monaco-editor的那些事儿~

是不是觉得开发一个编辑器这种事儿完全不可能?其实微软早就给出了答案,那就是本文的主角:monaco-editor,借助它我们可以实现自己的编辑器,废话不多说,来看看它到底是何方神圣吧~

聊聊monaco-editor的那些事儿~

什么是 monaco-editor

monaco-editor 是由微软从 vscode 中抽离出其核心代码而成,因此整体的界面风格以及使用体验是跟 vscode 基本一样的

monaco-editor 给开发者提供了丰富的接口供其使用,非常方便进行功能的扩展,常用接口列举如下

  • 编辑内容变化事件 onDidChangeModelContent
  • 注册代码补全 monaco.languages.registerCompletionItemProvider
  • 报错信息标注 monaco.editor.setModelMarkers
  • 获取当前编辑的内容:monacoInstance.getValue()

我们借助这些api是可以实现符合自己口味的编辑器的,但是扩展的成本还是比较高,那我们能不能更轻松地实现扩展呢?

聊聊monaco-editor的那些事儿~

其实社区上已经有很成熟的方案,而这个成熟的方案离不开下一节将要涉及的内容:LSP,它可以实现的特性包括且不限于

  • 代码语法检测
  • 代码的智能补全提示
  • 导入第三方包后,可以智能提示第三方包里的方法
  • 代码风格的格式化

什么是 LSP

LSP全称为 Language Server Protocol,它其实是微软于 2016 年定义的一套特定格式的协议,该协议定义了一套 编辑器或IDE语言服务器 之间使用的标准

之前各个编辑器(VSCode, Vim, Atom, Sublime...)各自为战,编辑器内部实现的特性和协议都不同,每换一个编辑器,就有可能要给该编辑器中支持的每门语言写一个对应的 Language Server,也就是说假设有 n 门语言,m 个编辑器,那全部编辑器适配所有语言的开发成本和复杂度为 n * m,而 LSP 的引入,可以将其复杂度降低为 n,可以看下图直观感受下 LSP 的威力

聊聊monaco-editor的那些事儿~

因此通过 LSP,我们可以以最小成本来适配不同语言和不同编辑器,更棒的是社区里已经提供了许多语言的 server,我们可以按照自己的需要进行选择

下面给出 LSP 中通信的数据格式

//请求
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "textDocument/codeAction",
  "params": {
    "textDocument": {
      "uri": "inmemory://model/1"
    },
    "range": {
      "start": {
        "line": 0,
        "character": 1
      },
      "end": {
        "line": 0,
        "character": 1
      }
    },
    "context": {
      "diagnostics": [],
      "triggerKind": 2
    }
  }
}

//响应
{
  "id": 2,
  "result": [
    {
      "title": "Execute Query",
      "command": "executeQuery",
      "arguments": [
        "inmemory://model/1"
      ]
    },
    {
      "title": "Show Databases",
      "command": "showDatabases"
    },
    {
      "title": "Show Schemas",
      "command": "showSchemas"
    },
    {
      "title": "Show Connections",
      "command": "showConnections"
    },
    {
      "title": "Switch Database",
      "command": "switchDatabase"
    },
    {
      "title": "Switch Connections",
      "command": "switchConnections"
    }
  ],
  "jsonrpc": "2.0"
}

可以看到 LSP 是以 JSON 的格式进行交互,且有几个关键字段,下面一一讲解

  • jsonrpc: 指定 LSP 版本
  • id: 这个字段是请求和响应一一对应的,LSP 客户端发出的不同请求,同响应里的 id 字段进行对应,从而找到正确的响应内容
  • method:客户端不同的操作,会触发不同的事件,那么这个字段就是告诉服务端,客户端进行了什么操作,从而服务端能做出正确的响应
  • params:请求里所携带的参数
  • result:响应里的数据存放的地方

一般来说,客户端和服务端是通过 websocket 进行通信,从而更好地提高使用丝滑度

实现方案

我们现在有了LSP的server,那么在client端也要做对应的调整,经过我之前的摸索,一套基于LSP可行的实现方案如下

  • monaco-editor:编辑器主体
  • monaco-languageclient:提供与 language server 交互的能力
  • vscode-ws-jsonrpc:提供以 websocket 为基础的 jsonrpc 调用
  • vite-plugin-monaco-editor:用于给内置的 js,ts,css,html 提供代码语法检测与补全
  • dt-sql-parser:由于 sqls 不支持代码的语法检测,因此使用该库来补全缺失的功能
  • python-lsp-server,用于支持 python,通过 pylsp --ws --port [port]启动
  • sqls,用于支持 sql
  • jsonrpc-ws-proxy,由于 sqls 只支持标准输入输出流的交互,因此使用该库来支持 websocket 的交互方式

结语

编辑器的开发,本身是极具复杂度的,但还好有社区的支持,才能让我们比较轻易地实现自己的编辑器,我也从中深刻地体会到开源的伟大,这里引用一句话作为结尾吧:只有站在巨人的肩膀上,才能看得更远!

转载自:https://juejin.cn/post/7202537103933800506
评论
请登录