2

我想在浏览器中突出显示 Python 语法错误。

我发现有Python 的 LSP 实现Monaco 编辑器的 LSP 客户端

有没有办法将它们连接在一起?

4

1 回答 1

1

有一种方法可以将它们连接在一起!
这都是关于语言服务器协议的。

第一步:语言服务器

您需要的第一件事是一个正在运行的服务器,它将提供特定于语言的逻辑(例如自动完成、验证等)。
如您的问题中所述,您可以使用palantir 的 python-language-server
您还可以在langserver.org上按语言找到现有语言服务器实现的列表。

在 LSP 中,客户端和服务器旨在通过JSON-RPC websocket 进行通信。
您可以使用此 Python 脚本在您的设备上启动 Python 语言服务器:

python langserver_ext.py

这将在ws://localhost:3000/python.

第二步:语言客户端

Monaco 最初是 VSCode 的一部分。因此,大多数现有的摩纳哥 LSP 客户端部分最初都是为 VSCode 设计的,因此您需要使用一些 nodejs 和 npm。

有很多模块可以连接 monaco 和 LSP 客户端,有些带有 vscode,有些则没有 - 解决这个问题变得非常耗时。
这是我使用并最终开始工作的模块列表:

在浏览器上使用服务器端 JavaScript

现在,整洁的部分:节点模块是服务器端的javascript。这意味着,您不能直接在浏览器中使用它们(请参阅无法在客户端(浏览器)上使用 RequireJS 来访问来自 node_modules 的文件。)。
您需要使用构建工具,例如browserify来转换您的节点模块以供浏览器使用:

.../node_modules/@codingame/monaco-languageclient/lib$ browserify monaco-language-client.js monaco-services.js connection.js ../../monaco-jsonrpc/lib/connection.js -r ./vscode-compatibility.js:vscode > monaco-jsonrpc-languageclient.js

这将创建一个名为 的文件monaco-jsonrpc-languageclient.js,我们将把它用作 monaco-languageclient 和 monaco-jsonrpc 的包。
笔记:

  • -r ./vscode-compatibility.js:vscode告诉browserify 为模块的每个依赖项使用该文件(请参阅包装vscode-compatibility.jsmonaco -languageclient 时未解决的对 'vscode' 的依赖项)。vscode
  • 我们将这些模块浏览为单个包,以避免包含多个依赖项。

现在您有了一个与浏览器兼容的 javascript 文件,您需要使所需的组件可见(即,将它们导出为window属性)。
monaco-jsonrpc-languageclient.js中,搜索导出 MonacoLanguageClient、createConnection、MonacoServices、listen、ErrorAction 和 CloseAction 的地方。在那里,添加一行以将它们导出:

(...)
exports.MonacoLanguageClient = MonacoLanguageClient;
window.MonacoLanguageClient = MonacoLanguageClient; // Add this line
(...)
exports.createConnection = createConnection;
window.createConnection = createConnection; // Add this line
(...)
(MonacoServices = exports.MonacoServices || (exports.MonacoServices = {}));
window.MonacoServices = MonacoServices; // Add this line
(...)
etc.

对 normalize-url 执行相同的操作:

.../node_modules/normalize-url/lib$ browserify index.js > normalizeurl.js

normalizeurl.js中,搜索导出 normalizeUrl 的地方。在那里(或者,默认情况下,在文件末尾),添加一行以全局导出它:

window.normalizeUrl = normalizeUrl;

你可以对reconnecting-websocket做同样的操作,或者使用模块自带的amd版本。

在您的页面上包含monaco-jsonrpc-languageclient.js,normalizeurl.js和 browserified 或 AMD reconnecting-websocket 模块。
为了加快加载速度,您还可以使用缩小工具(如uglify-js)缩小它们。

最后,我们可以创建并连接客户端:

// From https://github.com/TypeFox/monaco-languageclient/blob/master/example/src/client.ts
/* --------------------------------------------------------------------------------------------
 * Copyright (c) 2018 TypeFox GmbH (http://www.typefox.io). All rights reserved.
 * Licensed under the MIT License. See License.txt in the project root for license information.
 * ------------------------------------------------------------------------------------------ */
 
MonacoServices.install(monaco); // This is what links everything with your monaco editors.

var url = 'ws://localhost:3000/python';
// Create the web socket.
var webSocket = new ReconnectingWebSocket(normalizeUrl(url), [], {
    maxReconnectionDelay: 10000,
    minReconnectionDelay: 1000,
    reconnectionDelayGrowFactor: 1.3,
    connectionTimeout: 10000,
    maxRetries: Infinity,
    debug: false
});

// Listen when the web socket is opened.
listen({
    webSocket,
    onConnection: function(connection) {
        // create and start the language client
        var languageClient = new MonacoLanguageClient({
            name: 'Python Language Client',
            clientOptions: {
                // use a language id as a document selector
                documentSelector: ['python'],
                // disable the default error handler
                errorHandler: {
                    error: () => ErrorAction.Continue,
                    closed: () => CloseAction.DoNotRestart
                }
            },
            // create a language client connection from the JSON RPC connection on demand
            connectionProvider: {
                get: (errorHandler, closeHandler) => {
                    return Promise.resolve(createConnection(connection, errorHandler, closeHandler));
                }
            }
        });
        var disposable = languageClient.start();
        connection.onClose(() => disposable.dispose());
    }
});
于 2022-03-04T10:15:25.680 回答