如何创建集成 LSP 支持多语言的 Web 代码编辑器
对于一个云开发平台来说,一个好的 Web IDE
能很大程度地提高用户的编码体验,而一个 Web IDE
的一个重要组成部分就是代码编辑器。
目前有着多款 web 上的代码编辑器可供选择,比如 Ace
、CodeMirror
、Monaco
,这三款编辑器的比较在这篇文章中有着详细的介绍,在此就不作过多赘述。这篇文章我们选择 Monaco Editor
来对 LSP 进行集成,从而在理论上能够支持所有的编程语言。
什么是 LSP
LSP(Language Server Protocol),也就是语言服务协议,更具体更通俗地说就是定义了在代码编辑器和语言服务器之间的一套规范,从而让原本
m
个编辑器与 n
个编程语言之间的对应关系
变为
m
个编辑器与 LSP
的关系和 n
个编程语言与 LSP
之间的关系,
从而将开发的复杂度由 m*n
降到了 m+n
。
除了对编辑器开发者和编程语言开发者友好,对我们这种尝试让一个编辑器支持多种语言的开发者也更是友好,有 vscode
这样的编辑器珠玉在前,便能轻松地根据 vscode
的设计思路实现我们的需求。
预览
在这篇文章中,我们会开发一个最小最轻量的编辑器 Demo
作为演示,架构非常简单,就是前端创建一个 Monaco Editor
,后端创建一个语言服务器,二者之间通过 vscode-ws-jsonrpc
和 WebSocket
服务进行传输,实际实现的 Web
端 Python
编辑器如下:
Server 端开发
在 Web
端能接入语言服务前,我们得先在服务端运行一个语言服务,https://langserver.org/ 这个网站收录了许多语言服务的实现,
这里我们选择微软官方维护的 pyright
提供语言服务
首先创建 Express
服务器,配置静态文件服务,使用 fileURLToPath
和 dirname
来获取当前文件的路径,并将服务设置在 30000
端口
const app = express();
const __filename = fileURLToPath(import.meta.url);
const dir = dirname(__filename);
app.use(express.static(dir));
const server = app.listen(30000);
然后我们需要创建一个 WebSocket Server,注意这里的 noServer 参数,如果没有指定 noServer,那么 WebSocketServer 会自动创建一个 http server 来处理浏览器的 HTTP 请求到 WebSocket 请求的 upgrade。
const wss = new WebSocketServer({
noServer: true,
});
而这里我们需要创建自己的 HTTP 服务器,并手动处理浏览器的 upgrade 请求。下面代码便是如何监听 upgrade 事件并进行处理。
server.on('upgrade',()=>{});
在处理函数中,按照下面的代码将 WebSocket 使用到 jsonrpc 协议中,并启动语言服务器让二者相连。
先构建语言服务器的路径,找到 pyright 包所在的位置。
const baseDir = resolve(getLocalDirectory(import.meta.url));
const relativeDir = '../../../node_modules/pyright/dist/pyright-langserver.js';
const ls = resolve(baseDir, relativeDir);
再创建语言服务器的连接 和 创建 WebSocket 的数据连接
const serverConnection = createServerProcess(serverName, 'node', [ls, '--stdio']);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
const socketConnection = createConnection(reader, writer, () => socket.dispose());
最后用 forward 函数将消息从 soketConnection 转发到 serverConnection,如下:
forward(socketConnection, serverConnection, message => {
if (Message.isRequest(message)) {
console.log(`Received:`);
console.log(message);
}
if (Message.isResponse(message)) {
console.log(`Sent:`);
console.log(message);
}
return message;
});
于是我们将语言服务器跑起来,并在文章后面阶段会写的前端编辑器中随便输入一点东西,可以看到终端里输出了 message,
这样我们就使用 pyright 完成了语言服务器的开发。
Web 端开发
接下来我们开发前端的内容,还是在上面的那个网站中,
可以看到 Monaco Editor 也是有支持 LSP 的方案的,所以我们使用 TypeFox 开发的 monaco-languageclient 对monaco 进行集成 lsp 的开发。
首先使用 monaco-languageclient 的 initServices 函数初始化一些服务,其中最重要的就是下面四个配置,定义了 Monaco 的语言服务与主题显示。
await initServices({
enableModelService: true,
enableThemeService: true,
enableTextmateService: true,
enableLanguagesService: true,
})
然后创建能够与语言服务器相连的 WebSocket 连接。
createWebSocket("ws://localhost:30000/pyright");
而创建这个连接也需要用到 monaco-languageclient。
const createWebSocket = (url: string): WebSocket => {
const webSocket = new WebSocket(url);
webSocket.onopen = async () => {
const socket = toSocket(webSocket);
const reader = new WebSocketMessageReader(socket);
const writer = new WebSocketMessageWriter(socket);
languageClient = createLanguageClient({
reader,
writer
});
await languageClient.start();
reader.onClose(() => languageClient.stop());
};
return webSocket;
};
const createLanguageClient = (transports: MessageTransports): MonacoLanguageClient => {
return new MonacoLanguageClient({
name: 'Pyright Language Client',
clientOptions: {
documentSelector: [languageId],
errorHandler: {
error: () => ({ action: ErrorAction.Continue }),
closed: () => ({ action: CloseAction.DoNotRestart })
},
workspaceFolder: {
index: 0,
name: 'workspace',
uri: monaco.Uri.parse('/tmp')
},
synchronize: {
fileEvents: [vscode.workspace.createFileSystemWatcher('**')]
}
},
connectionProvider: {
get: () => {
return Promise.resolve(transports);
}
}
});
};
接下来这里需要创建一个虚拟文件系统,作为 Monaco Editor 实例的输入输出。
const fileSystemProvider = new RegisteredFileSystemProvider(false);
fileSystemProvider.registerFile(new RegisteredMemoryFile(vscode.Uri.file('/test.py'), 'print("Hello, laf!")'));
registerFileSystemOverlay(1, fileSystemProvider);
const modelRef = await createModelReference(monaco.Uri.file('/test.py'));
最后创建 Monaco Editor 实例即可,还能进行些许的配置。
createConfiguredEditor(document.getElementById('container')!, {
model: modelRef.object.textEditorModel,
automaticLayout: true,
minimap: {
enabled: false
},
scrollbar: {
verticalScrollbarSize: 4,
horizontalScrollbarSize: 8,
},
overviewRulerLanes: 0,
lineNumbersMinChars: 4,
scrollBeyondLastLine: false,
theme: 'vs',
});
就这样我们也完成了前端。
import Editor from './python/Editor';
function App() {
return (
<>
<h2>monaco python lsp</h2>
<div style={{height:"500px", width:"800px", border:"1px solid black", padding:"8px 0"}}>
<Editor />
</div>
</>
)
}
export default App
效果如下
小结
要深入理解 LSP 以及其背后的工作原理还是有很大的难度的,但是好在有 languageserver,languageclient 这类优秀的开源项目提供支持,能够让我们在仅仅拼凑了几段代码后拥有不错的代码编辑器效果。下一步计划用 LSP 改造 Laf 的 Web IDE。
由于我也只是刚刚接触这块知识,文章中难免有错漏,希望能与读到这里的各位共同交流进步。
参考资料
- https://github.com/microsoft/language-server-protocol/wiki/Protocol-History
- https://medium.com/@malintha1996/understanding-the-language-server-protocol-5c0ba3ac83d2
- https://ubug.io/blog/workpad-part-6
- https://www.typefox.io/blog/how-to-embed-a-monaco-editor-in-a-browser-as-a-part-of-my-first-task-at-typefox
- https://www.typefox.io/blog/teaching-the-language-server-protocol-to-microsofts-monaco-editor
如何创建集成 LSP 支持多语言的 Web 代码编辑器的更多相关文章
- Win10 UWP 开发系列:使用多语言工具包让应用支持多语言
之前我在一篇blog中写过如何使用多语言工具包,见http://www.cnblogs.com/yanxiaodi/p/3800767.html 在WinEcos社区也发布过一篇详细的文章介绍多语言工 ...
- ASP.NET 网站支持多语言
ASP.NET网站支持多语言 (本地资源文件和全局资源文件的调用及需注意的地方总结) 一. 本地资源文件(App_LocalResources): ①. 本地资源的生成及调用 1.本地 ...
- 让你的.NET程序支持多语言
辛辛苦苦做出来的软件,我们当然希望能让更多的人用,支持多语言是必须的.下面我将以Asp.net Web Form为例来介绍如何支持多语言.其他程序比如windows程序,过程都是大同小异的. 大概分以 ...
- 【Android Developers Training】 11. 支持不同语言
注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...
- FineUIPro中如何支持多语言(全局资源文件和本地资源文件)
一个客户在邮件中问到了FineUIPro的多语言实现问题,其实 FineUIPro 并没有对此做特殊处理,因此直接使用 ASP.NET 原生支持的资源文件就能实现. 下面我们就以FineUIPro的空 ...
- Qt 编写应用支持多语言版本--一个GUI应用示例
简介 上一篇博文已经说过如何编写支持多语言的Qt 命令行应用,这一篇说说Qt GUI 应用多语言支持的坑. 本人喜欢用代码来写布局,而不是用 Qt Designer 来设计布局,手写布局比 Qt De ...
- django支持多语言
Django支持多语言切换 下面介绍下如何使网站或APP国际化,让其支持多种语言 . 官网 效果 1.创建locale文件夹 先在项目根目录下创建一个名为locale的文件夹,这个文件夹是用来存放dj ...
- C# 动态创建类,动态创建表,支持多库的数据库维护方案
1.创建表 SqlSugar支持了3种模式的建表,非常的灵活,可以MYSQL MSSQL ORACLE等用同一语法创建数据库,不需要考虑数据库的兼容性 中间标准: string 大文本 5.1.3. ...
- XE3随笔21:系统默认语言与系统支持的语言列表
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, For ...
- 推荐一个算法编程学习中文社区-51NOD【算法分级,支持多语言,可在线编译】
最近偶尔发现一个算法编程学习的论坛,刚开始有点好奇,也只是注册了一下.最近有时间好好研究了一下,的确非常赞,所以推荐给大家.功能和介绍看下面介绍吧.首页的标题很给劲,很纯粹的Coding社区....虽 ...
随机推荐
- 用BP软件 批量注册用户
第五步:查看管理员后台----用户界面, 有没有批量添加进用户
- elment UI + EasyExcel 实现 导入
前端组件 <hd-flex> <el-dialog v-model="isUploadDialog" width="50%" lock-scr ...
- 使用yaml进行数据驱动
一.需求描述 1.请求登陆接口,从登陆接口的响应头数据中获取token值,并写入yml文件: 2.读取写入yml文件中的token值作为下个接口的传参,请求查询物料列表接口,查看查询结果. yaml_ ...
- 2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j。
2023-06-10:给定一个由 n 个节点组成的网络,用 n x n 个邻接矩阵 graph 表示 在节点网络中,只有当 graph[i][j] = 1 时,节点 i 能够直接连接到另一个节点 j. ...
- CtfShow信息收集1-20攻略
CtfShow信息收集1-20关攻略 简单更新一下CTF关于信息收集的题目,总共20道,网站地址:https://ctf.show/challenges 第一关 我们可以看到提示:开发注释没有及时删除 ...
- 【技术积累】C语言中基础知识【三】
什么是C语言[了解即可] C语言是一种通用的高级编程语言,由美国贝尔实验室的Dennis Ritchie在20世纪70年代早期开发出来的.它在计算机科学和软件工程领域中被广泛使用. C语言具有以下特点 ...
- [数据分析与可视化] 基于plottable库绘制精美表格
plottable是一个Python库,用于在matplotlib中绘制精美定制的图形表格.plottable的官方仓库地址为:plottable.本文主要参考其官方文档,plottable的官方文档 ...
- java使用SFTP连接服务器下载,上传文件
package mocha.framework.util; /* * @author Xiehj * @version 2019年10月28日 上午9:37:28 */ import java.io. ...
- 【转载】AF_XDP技术详解
原文信息 作者:rexrock 出处:https://rexrock.github.io/post/af_xdp1/ 目录 1. 用户态程序 1.1 创建AF_XDP的socket 1.2 为UMEM ...
- c语言分析和循坏对应的汇编定义格式(Debug版本)
c语言if单分支结构所对应的汇编代码结构 #include "stdafx.h" int main(int argc, char* argv[]) { if(argc > 8 ...