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可用:
- sourceRoot
- 模块
- 模块分辨率
- baseUrl
- 路径
- 根目录
- ...
但是我无法完成外部模块工作的代码。
有没有办法为 Monaco 编辑器设置某种 "source provider",所以一旦找到 require(...)
语句,它就会触发加载该模块并最终获得代码完成工作?我们已经成功地为 Orion 和 tern.js 实施了这种方法:ide-orion/editorBuild/commonjs-simplified
可在此处找到基于 Acorn.js 的参考实现:
服务器端集成:
- 服务器端模块,用于检索系统中的 JavaScript 模块并为每个导入的模块提供代码补全:dirigible/ide-monaco-extensions
- Acorn.js 飞船模块:acorn/acornjs
- 建议解析器:
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
客户端集成
- 检索系统中可用的 JavaScript 个模块:editor.html#L204-L212
- 获取每个模块的建议:editor.html#L215-L225
- 为
require(...)
添加额外的库:editor.html#L301
- 为模块注册完成项目提供者:editor.html#L302-L329
- 注册完成项提供者以获取建议(functions/properties):editor.html#L330-L367
我正在将 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可用:
- sourceRoot
- 模块
- 模块分辨率
- baseUrl
- 路径
- 根目录
- ...
但是我无法完成外部模块工作的代码。
有没有办法为 Monaco 编辑器设置某种 "source provider",所以一旦找到 require(...)
语句,它就会触发加载该模块并最终获得代码完成工作?我们已经成功地为 Orion 和 tern.js 实施了这种方法:ide-orion/editorBuild/commonjs-simplified
可在此处找到基于 Acorn.js 的参考实现:
服务器端集成:
- 服务器端模块,用于检索系统中的 JavaScript 模块并为每个导入的模块提供代码补全:dirigible/ide-monaco-extensions
- Acorn.js 飞船模块:acorn/acornjs
- 建议解析器:
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
客户端集成
- 检索系统中可用的 JavaScript 个模块:editor.html#L204-L212
- 获取每个模块的建议:editor.html#L215-L225
- 为
require(...)
添加额外的库:editor.html#L301 - 为模块注册完成项目提供者:editor.html#L302-L329
- 注册完成项提供者以获取建议(functions/properties):editor.html#L330-L367