Monaco 探索(一)

Monaco 探索(一)

六月 08, 2021 本文共计: 1.2k 字 预计阅读时长: 5分钟

小王最近接到一个需求是开发一个web ide,当然仅仅是代码编写与常用代码块联想。

国际惯例:调研,调研市面上是否有成熟的方案:

  • Monaco
  • cloud-server(说白了就是基于Monaco开发的)
  • 未知,当我看到Monacovscode的前身)的时候就基本止步了。

那么再看看有没有适合ReactMonaco方案:

万事具备,只欠东风!

先来介绍下LSP,不是老色皮的意思!!!

这里就不再赘述别人的文章了,直接看传送门

LSP参考https://zhuanlan.zhihu.com/p/139382598

Demo起步

monaco-languageclient

内置了各种demo,完全可以跑起来。

抽象工具

借用上述demo肯定是不行的,因为一个真正好的服务是像MVP思想一样,可拔插,可独立运行的。

因此:tool-monaco诞生了~~~

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
import { listen } from '@codingame/monaco-jsonrpc';

import {
MonacoLanguageClient, MessageConnection, CloseAction, ErrorAction,
MonacoServices, createConnection
} from 'monaco-languageclient';
const normalizeUrl = require('normalize-url');
const ReconnectingWebSocket = require('reconnecting-websocket').default;


require('monaco-editor');
window.MonacoEnvironment = {
getWorkerUrl: () => './editor.worker.bundle.js'
}

export const installEdit = (monaco) => {
window.monaco = monaco;
console.log(monaco, "monaco")
monaco.languages.register({
id: 'java',
extensions: ['.java'],
aliases: ['JAVA', 'java'],
mimetypes: ['text/x-java-source', 'text/x-java'],

});


const value = `sssss`;
monaco.editor.create(document.getElementById("test-edit")!, {
model: monaco.editor.createModel(value, 'java', monaco.Uri.parse('https://scholarself.oss-cn-beijing.aliyuncs.com/test/public/composer-executer-demo/src/main/java/com/yqn/framework/executer/Application.java')),
glyphMargin: true,
lightbulb: {
enabled: true
},
language: "java"
});

const ms = MonacoServices.install(monaco, {rootUri: 'https://scholarself.oss-cn-beijing.aliyuncs.com/test/public/'});

const url = createUrl('/sampleServer')
const webSocket = createWebSocket(url);
webSocket,
onConnection: connection => {
const languageClient = createLanguageClient(connection);
const disposable = languageClient.start();
ms.commands.registerCommand('java.show.references', function(...args: any[]) {
console.log(args);
})
ms.commands.registerCommand('java.apply.workspaceEdit', function(...args: any[]) {
applyWorkspaceEdit(args, languageClient)
})
connection.onClose(() => disposable.dispose());
}
});
}

function createLanguageClient(connection: MessageConnection): MonacoLanguageClient {
return new MonacoLanguageClient({
name: "Sample Language Client",
clientOptions: {
documentSelector: ['java'],
errorHandler: {
error: () => ErrorAction.Continue,
closed: () => CloseAction.DoNotRestart
}
},
connectionProvider: {
get: (errorHandler, closeHandler) => {
return Promise.resolve(createConnection(connection, errorHandler, closeHandler))
}
}
});
}

function createUrl(path: string): string {
return normalizeUrl("ws://xxxx")
}

function createWebSocket(url: string): WebSocket {
const socketOptions = {
maxReconnectionDelay: 10000,
minReconnectionDelay: 1000,
reconnectionDelayGrowFactor: 1.3,
connectionTimeout: 10000,
maxRetries: Infinity,
debug: true
};
return new ReconnectingWebSocket(url, [], socketOptions);
}

function applyWorkspaceEdit(args: any[], languageclient: MonacoLanguageClient) {
console.log(args);
}


上述代码就是一个基本的雏形,等待进一步的抽象、加工、设计。

下面核心阐述下我遇到的几个问题,几个困扰我超过24h的问题:

问题一

Cannot find module 'vscode'

waw!, 是因为缺少引用,我们可以在webpack.config.js中这样配置:

1
2
3
4
5
6
7
8
9
10
11

{
resolve: {
alias: {
vscode: path.resolve(__dirname, 'node_modules/monaco-languageclient/lib/vscode-compatibility'),
vs: path.resolve(__dirname, 'node_modules/monaco-editor/dev/vs'),
'monaco-editor': path.resolve(__dirname, 'node_modules/monaco-editor/esm/vs/editor/editor.api.js')
}
},
}

问题二

Cannot read property 'addCommand' of undefined

这是比较狗的一个问题,气得我想锤人。

1
2
3
4
5
6
7
8

TypeError: Cannot read property 'addCommand' of undefined
at MonacoCommands.registerCommand (monaco-commands.ts:12)
at Object.registerCommand (vscode-api.ts:788)
at ExecuteCommandFeature.register (client.js:1650)
at ExecuteCommandFeature.initialize (client.js:1630)
at MonacoLanguageClient.initializeFeatures (client.js:2462)
at client.js:2147

一眼看过去,咋回事呢???一顿debug,最终找到了罪恶之源:

这个文件在monaco-languageclient ---> lib ----> monaco-commands.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
class MonacoCommands {
constructor(editor) {
this.editor = editor;
}
registerCommand(command, callback, thisArg) {
return this.editor._commandService.addCommand({
id: command,
handler: (_accessor, ...args) => callback(...args)
});
}
}
exports.MonacoCommands = MonacoCommands;
//# sourceMappingURL=monaco-commands.js.map

原来是this.editor中不存在_commandService这个对象。。。.

我开始怀疑一些东西,但是我不确定,我开始debug官方案例的demo

然后我发现demo中是这么干的:

1
2
3
4
5
6
7
8
9
10
11
12
13
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.MonacoCommands = void 0;
class MonacoCommands {
constructor(_monaco) {
this._monaco = _monaco;
}
registerCommand(command, callback, thisArg) {
return this._monaco.editor.registerCommand(command, (accessor, ...args) => callback.call(thisArg, ...args));
}
}
exports.MonacoCommands = MonacoCommands;
//# sourceMappingURL=monaco-commands.js.map

但是很明显,现在的stable版本的monaco-languageclient没这个东西???!!!

官方,做个人好么!!!

正巧不巧,我在npm仓库中看到了有next版本:

找到了最新的一个0.13.1-next.9,最终解决。

问题三

Uncaught (in promise) Error: Unknown parameter structure byName

又来了这种莫名奇妙的问题,经历了一番寻找之后:

1
2
3

$ npm ls vscode-jsonrpc

1
$  npm dedupe vscode-jsonrpc

最后,给仓库贡献一个issue,方便别的开发者出问题踩坑,迅速定位问题:

Ahhh,但行好事,莫问前程!

到此,一个简陋版本的(没有任何环境问题的IDE)就搭建好了。

两天后分享第二篇,并附封装详细代码解读~~~