聊聊monaco-editor的那些事儿~
想必大家在平常的工作中,编辑器一定是打交道最多的工具,那你有没有想过自己开发一款编辑器呢?
是不是觉得开发一个编辑器这种事儿完全不可能?其实微软早就给出了答案,那就是本文的主角:monaco-editor,借助它我们可以实现自己的编辑器,废话不多说,来看看它到底是何方神圣吧~
什么是 monaco-editor
monaco-editor 是由微软从 vscode 中抽离出其核心代码而成,因此整体的界面风格以及使用体验是跟 vscode 基本一样的
monaco-editor 给开发者提供了丰富的接口供其使用,非常方便进行功能的扩展,常用接口列举如下
- 编辑内容变化事件 onDidChangeModelContent
- 注册代码补全 monaco.languages.registerCompletionItemProvider
- 报错信息标注 monaco.editor.setModelMarkers
- 获取当前编辑的内容:monacoInstance.getValue()
我们借助这些api是可以实现符合自己口味的编辑器的,但是扩展的成本还是比较高,那我们能不能更轻松地实现扩展呢?
其实社区上已经有很成熟的方案,而这个成熟的方案离不开下一节将要涉及的内容:LSP,它可以实现的特性包括且不限于
- 代码语法检测
- 代码的智能补全提示
- 导入第三方包后,可以智能提示第三方包里的方法
- 代码风格的格式化
什么是 LSP
LSP全称为 Language Server Protocol,它其实是微软于 2016 年定义的一套特定格式的协议,该协议定义了一套 编辑器或IDE 与 语言服务器 之间使用的标准
之前各个编辑器(VSCode, Vim, Atom, Sublime...)各自为战,编辑器内部实现的特性和协议都不同,每换一个编辑器,就有可能要给该编辑器中支持的每门语言写一个对应的 Language Server,也就是说假设有 n 门语言,m 个编辑器,那全部编辑器适配所有语言的开发成本和复杂度为 n * m,而 LSP 的引入,可以将其复杂度降低为 n,可以看下图直观感受下 LSP 的威力
因此通过 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