Monaco - 没有 LSP 的 CommonJS 模块的代码完成

Monaco - Code Completion for CommonJS Modules without LSP

我正在将 Monaco 编辑器集成到 Eclipse Dirigible Web IDE 中。

目前编辑器是这样集成的:ide-monaco/editor.html

在 Dirigible 中,我们使用服务器端 JavaScript,基于 Mozila Rhino、Nashorn、J2V8 或 GraalVM(非 NodeJS)作为目标编程语言。

为了实现模块化,我们根据CommonJS规范require(...moduleName..)加载模块。

这是我们拥有的此类模块 (API) 的示例:

下面是这个 API 的示例用法:

现在回到摩纳哥主题,我正在尝试为加载的模块实现代码完成,例如:

var response = require("http/v4/response");
...

我找到了一个关于如何提供外部库的示例:

monaco.languages.typescript.javascriptDefaults.addExtraLib('var response = {println: /** Prints the text in the response */ function(text) {}}', 'js:response.js');

Dirigible Monaco Code Completion with Extra Lib

但是一旦声明了 var response,它就会隐藏代码完成选项:

Dirigible Monaco Shadowed Code Completion Options

我发现有几个摩纳哥CompilerOptions可用:

但是我无法完成外部模块工作的代码。

有没有办法为 Monaco 编辑器设置某种 "source provider",所以一旦找到 require(...) 语句,它就会触发加载该模块并最终获得代码完成工作?我们已经成功地为 Orion 和 tern.js 实施了这种方法:ide-orion/editorBuild/commonjs-simplified

可在此处找到基于 Acorn.js 的参考实现:

  1. 服务器端集成:

    exports.parse = function(moduleName) {
        var content = contentManager.getText(moduleName + ".js");
        var comments = [];
        var nodes = acorn.parse(content, {
            onComment: comments,
            ranges: true
        });
    
        var functionDeclarations = nodes.body
            .filter(e => e.type === "FunctionDeclaration")
            .map(function(element) {
                let name = element.id.name;
                let expression = element.expression
                let functions = element.body.body
                    .filter(e => e.type === "ExpressionStatement")
                    .map(e => extractExpression(e, comments))
                    .filter(e => e !== null);
                return {
                    name: name,
                    functions: functions
                }
            });
    
        var result = nodes.body
            .filter(e => e.type === "ExpressionStatement")
            .map(function(element) {
                return extractExpression(element, comments, functionDeclarations);
            }).filter(e => e !== null);
    
        return result;
    }
    
    function extractExpression(element, comments, functionDeclarations) {
        let expression = element.expression;
        if (expression && expression.type === "AssignmentExpression" && expression.operator === "=") {
            let left = expression.left;
            let right = expression.right;
            if (right.type === "FunctionExpression") {
                let properties = right.params.map(e => e.name);
                let name = left.property.name + "(" + properties.join(", ") + ")"; 
                let documentation = extractDocumentation(comments, element, name);
                documentation = formatDocumentation(documentation, name, true);
                let returnStatement = right.body.body.filter(e => e.type === "ReturnStatement")[0];
                let returnType = null;
                if (functionDeclarations && returnStatement && returnStatement.argument.type === "NewExpression") {
                    returnType = returnStatement.argument.callee.name;
                    returnType = functionDeclarations.filter(e => e.name === returnType)[0];
                }
                return {
                    name: name,
                    documentation: documentation,
                    returnType: returnType,
                    isFunction: true
                };
            } else if (right.type === "Literal") {
                let name = left.property.name;
                let documentation = extractDocumentation(comments, element, name);
                documentation = formatDocumentation(documentation, name, false);
                return {
                    name: name,
                    documentation: documentation,
                    isProperty: true
                };
            }
        }
        return null;
    }
    ...
    

    The complete file can be found here: suggestionsParser.js

  2. 客户端集成