Commit a389df1b authored by Jérome Perrin's avatar Jérome Perrin

monaco_editor: WIP reference provider for python

parent b1a3ebfc
......@@ -414,6 +414,62 @@ def ERP5Site_getPythonSourceCodeCompletionList(self, data, REQUEST=None):
logger.info(
"jedi got %d completions in %.2fs", len(completions),
(time.time() - start))
if data.get('xxx_definition'):
completions = [] # XXX this is not "completions" ...
# TODO: only follow imports for classes, methods or functions ?
for definition in script.goto_definitions(
# follow_imports=True,
# only_stubs=False,
# prefer_stubs=False,
):
definition_line = definition.line
definition_column = definition.column
definition_uri = definition.module_path
definition_code = None
if script.path == definition.module_path:
definition_uri = None # client side will understand this as "current file"
if is_python_script:
# If we are in a python script and symbol is defined in the python script,
# we have to account for the 3 lines headers we added and the 2 columns of
# indentation
definition_line = definition_line - 3
definition_column = definition_column - 2
else:
if os.path.exists(definition.module_path):
with open(definition.module_path) as f:
definition_code = f.read()
# strip common prefix from Products or eggs directory
products_base_dir = os.path.join(
Products.ERP5Type.__file__, '..', '..')
prefix = os.path.commonprefix(
(products_base_dir, definition.module_path))
if prefix:
definition_uri = definition.module_path[len(prefix):]
if definition_uri.startswith('eggs/'):
definition_uri = definition_uri[len('eggs/'):]
elif definition_uri.startswith('develop-eggs/'):
definition_uri = definition_uri[len('develop-eggs/'):]
completions.append(
{
"range":
{
'startColumn':
1 + definition_column,
'endColumn':
1 + definition_column + len(definition.name),
'startLineNumber':
definition_line,
'endLineNumber':
definition_line,
},
'uri': definition_uri,
'code': definition_code,
})
if data.get('xxx_hover'):
completions = '' # XXX this is not "completions" ...
for definition in script.goto_definitions():
......
......@@ -388,6 +388,74 @@
},
});
monaco.languages.registerDefinitionProvider('python', {
provideDefinition: async function (model, position, token) {
const controller = new AbortController();
token.onCancellationRequested(() => {
controller.abort();
});
const data = new FormData();
const complete_parameters = {
code: model.getValue(),
position: {
line: position.lineNumber,
column: position.column,
},
};
complete_parameters['xxx_definition'] = true;
data.append('data', JSON.stringify(complete_parameters));
// TODO: this should use a proper endpoint ...
return fetch(
gadget.state.language_support_url +
'/ERP5Site_getPythonSourceCodeCompletionList',
{
method: 'POST',
body: data,
signal: controller.signal,
}
)
.then((response) => response.json())
.then(
(data) => {
var definitions = [];
for (let i = 0; i < data.length; i++) {
if (data[i].code) {
// TODO: these models are not refreshed, if the file they refefer is modified,
// they show outdated content.
let definition_uri = monaco.Uri.from({
scheme: 'file',
path: data[i].uri,
});
let definition_model = monaco.editor.getModel(
definition_uri
);
if (!definition_model) {
definition_model = monaco.editor.createModel(
data[i].code,
'python',
definition_uri
);
}
data[i].uri = definition_model.uri;
}
definitions.push({
range: data[i].range,
uri: data[i].uri ? data[i].uri : model.uri,
});
}
return definitions;
},
(e) => {
if (!(e instanceof DOMException) /* AbortError */) {
throw e;
}
/* ignore aborted requests */
}
);
},
});
this.runPyLint();
}
}
......
......@@ -309,6 +309,71 @@ $script.onload = function() {
'python',
yapfDocumentFormattingProvider)
monaco.languages.registerDefinitionProvider('python', {
provideDefinition: async function(model, position, token) {
const controller = new AbortController();
token.onCancellationRequested(() => {controller.abort()})
const data = new FormData();
const complete_parameters = {
code: model.getValue(),
position: {line: position.lineNumber, column: position.column}
};
// ZMI python scripts pass extra parameters to linter
if (bound_names) {
complete_parameters["script_name"] = script_name;
complete_parameters["bound_names"] = JSON.parse(bound_names);
complete_parameters["params"] = document.querySelector(
'input[name="params"]'
).value;
}
complete_parameters['xxx_definition'] = true;
data.append("data", JSON.stringify(complete_parameters));
return fetch(portal_url + "/ERP5Site_getPythonSourceCodeCompletionList", {
method: "POST",
body: data,
signal: controller.signal
})
.then(response => response.json())
.then(data => {
var definitions = [];
for (let i = 0; i < data.length; i++) {
if (data[i].code) {
// TODO: these models are not refreshed, if the file they refefer is modified,
// they show outdated content.
let definition_uri = monaco.Uri.from({
scheme: 'file',
path: data[i].uri,
});
let definition_model = monaco.editor.getModel(
definition_uri
);
if (!definition_model) {
definition_model = monaco.editor.createModel(
data[i].code,
'python',
definition_uri
);
}
data[i].uri = definition_model.uri;
}
definitions.push({
range: data[i].range,
uri: data[i].uri ? data[i].uri : model.uri,
});
}
return definitions;
}, e => {
if (!(e instanceof DOMException) /* AbortError */ ) {
throw e;
}
/* ignore aborted requests */
});
}
});
monaco.languages.registerCompletionItemProvider('python', {
provideCompletionItems: async function(model, position, context, token) {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment