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

monaco_editor: integrate yapf as python formatter

triggered manually with "Format Document" of "Format Selection"
actions
parent 085cdc4e
Pipeline #25469 failed with stage
in 0 seconds
import ast import ast
import enum import enum
import json import json
import tempfile
import textwrap
import logging
logger = logging.getLogger(__name__)
class SymbolKind(enum.IntEnum): class SymbolKind(enum.IntEnum):
...@@ -103,3 +108,36 @@ def ERP5Site_getPythonCodeSymbolList(self, data, REQUEST=None): ...@@ -103,3 +108,36 @@ def ERP5Site_getPythonCodeSymbolList(self, data, REQUEST=None):
if REQUEST: if REQUEST:
REQUEST.RESPONSE.setHeader('content-type', 'application/json') REQUEST.RESPONSE.setHeader('content-type', 'application/json')
return json.dumps(symbols) return json.dumps(symbols)
def ERP5Site_formatPythonSourceCode(self, data, REQUEST=None):
from yapf.yapflib import yapf_api
if isinstance(data, basestring):
data = json.loads(data)
try:
extra = {}
if data['range']:
extra['lines'] = (
(data['range']['startLineNumber'], data['range']['endLineNumber']), )
with tempfile.NamedTemporaryFile(mode='w', suffix='.style.yapf') as f:
f.write(
textwrap.dedent(
'''
[style]
based_on_style = pep8
indent_width = 2
continuation_indent_width = 2
split_before_expression_after_opening_paren = true
split_before_first_argument = true
split_before_arithmetic_operator = true
'''))
f.flush()
formatted_code, changed = yapf_api.FormatCode(
data['code'], style_config=f.name, **extra)
except SyntaxError as e:
logger.exception("Error in source code")
return json.dumps(dict(error=True, error_line=e.lineno))
if REQUEST is not None:
REQUEST.RESPONSE.setHeader('content-type', 'application/json')
return json.dumps(dict(formatted_code=formatted_code, changed=changed))
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ExternalMethod" module="Products.ExternalMethod.ExternalMethod"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_function</string> </key>
<value> <string>ERP5Site_formatPythonSourceCode</string> </value>
</item>
<item>
<key> <string>_module</string> </key>
<value> <string>MonacoEditorUtils</string> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>ERP5Site_formatPythonSourceCode</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <string></string> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
...@@ -235,6 +235,75 @@ ...@@ -235,6 +235,75 @@
'python', 'python',
documentSymbolProvider documentSymbolProvider
); );
const gadget = this;
const yapfDocumentFormattingProvider = {
_provideFormattingEdits: function (model, range, options, token) {
const controller = new AbortController();
token.onCancellationRequested(() => {
controller.abort();
});
const data = new FormData();
data.append(
'data',
JSON.stringify({ code: model.getValue(), range: range })
);
return fetch(
new URL(
'ERP5Site_formatPythonSourceCode',
location.href
).toString(),
{
method: 'POST',
body: data,
signal: controller.signal
}
)
.then((response) => response.json())
.then(
(data) => {
if (data.error) {
gadget.editor.revealLine(data.error_line);
return;
}
if (data.changed) {
return [
{
range: model.getFullModelRange(),
text: data.formatted_code
}
];
}
},
(e) => {
if (!(e instanceof DOMException) /* AbortError */) {
throw e;
}
/* ignore aborted requests */
}
);
},
provideDocumentRangeFormattingEdits: function (
model,
range,
options,
token
) {
return this._provideFormattingEdits(model, range, options, token);
},
provideDocumentFormattingEdits: function (model, options, token) {
return this._provideFormattingEdits(model, null, options, token);
}
};
monaco.languages.registerDocumentFormattingEditProvider(
'python',
yapfDocumentFormattingProvider
);
monaco.languages.registerDocumentRangeFormattingEditProvider(
'python',
yapfDocumentFormattingProvider
);
} }
if (modification_dict.hasOwnProperty('editable')) { if (modification_dict.hasOwnProperty('editable')) {
......
<?xml version="1.0"?>
<ZopeData>
<record id="1" aka="AAAAAAAAAAE=">
<pickle>
<global name="ZopePageTemplate" module="Products.PageTemplates.ZopePageTemplate"/>
</pickle>
<pickle>
<dictionary>
<item>
<key> <string>_bind_names</string> </key>
<value>
<object>
<klass>
<global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
</klass>
<tuple/>
<state>
<dictionary>
<item>
<key> <string>_asgns</string> </key>
<value>
<dictionary>
<item>
<key> <string>name_subpath</string> </key>
<value> <string>traverse_subpath</string> </value>
</item>
</dictionary>
</value>
</item>
</dictionary>
</state>
</object>
</value>
</item>
<item>
<key> <string>content_type</string> </key>
<value> <string>text/html</string> </value>
</item>
<item>
<key> <string>expand</string> </key>
<value> <int>0</int> </value>
</item>
<item>
<key> <string>id</string> </key>
<value> <string>testCodeEditorPythonFormatter</string> </value>
</item>
<item>
<key> <string>output_encoding</string> </key>
<value> <string>utf-8</string> </value>
</item>
<item>
<key> <string>title</string> </key>
<value> <unicode>Code Editor Python Formatter</unicode> </value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
<html xmlns:tal="http://xml.zope.org/namespaces/tal" xmlns:metal="http://xml.zope.org/namespaces/metal">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title tal:content="template/title_and_id"></title>
</head>
<body>
<table cellpadding="1" cellspacing="1" border="1">
<thead>
<tr>
<td rowspan="1" colspan="3" tal:content="template/title_and_id"></td>
</tr>
</thead>
<tbody>
<tal:block metal:use-macro="here/Zuite_CommonTemplate/macros/init" />
<tr>
<td>open</td>
<td>${base_url}/foo_module/ListBoxZuite_reset</td>
<td></td>
</tr>
<tr>
<td>assertTextPresent</td>
<td>Reset Successfully.</td>
<td></td>
</tr>
<tr>
<td colspan="3"><b>Set preferred code editor</b></td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/portal_preferences/erp5_ui_test_preference/Preference_viewHtmlStyle</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//input[@name="field_my_preferred_source_code_editor" and @value="monaco"]</td>
<td></td>
</tr>
<tr>
<td>clickAndWait</td>
<td>//button[@name='Base_edit:method']</td>
<td></td>
</tr>
<tr>
<td colspan="3"><b>Switch to renderjs UI and edit components</b></td>
</tr>
<tr>
<td>open</td>
<td>${base_url}/web_site_module/renderjs_runner/#/portal_components?editable=true</td>
<td></td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_app_loaded" />
<tr>
<td>waitForElementPresent</td>
<td>//a[@data-i18n='Add']</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>link=Add</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//select[@name='field_your_select_action']</td>
<td></td>
</tr>
<tr>
<td>select</td>
<td>//select[@name='field_your_select_action']</td>
<td>label=Document Component</td>
</tr>
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/submit_dialog" />
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Object created.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr>
<td colspan="3"><b>Wait for editor to be loaded and edit</b></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>//div[@data-gadget-scope="editor"]//iframe</td>
<td></td>
</tr>
<tr>
<td>selectFrame</td>
<td>//div[@data-gadget-scope="editor"]//iframe</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>css=div.monaco-editor.vs</td>
<td></td>
</tr>
<tr>
<td>storeEval</td>
<td>selenium.browserbot.getCurrentWindow().document.querySelector('div.monaco-editor.vs').getAttribute('data-uri')
</td>
<td>model-data-uri</td>
</tr>
<tr>
<td>assertEval</td>
<td>selenium.browserbot.getCurrentWindow().monaco.editor.getModel(storedVars['model-data-uri']).setValue("")
</td>
<td>null</td>
</tr>
<tr>
<td>selectFrame</td>
<td>relative=top</td>
<td></td>
</tr>
<tr>
<td>click</td>
<td>//button[@data-i18n="Save"]</td>
<td></td>
</tr>
<tal:block tal:define="notification_configuration python: {'class': 'success',
'text': 'Data updated.'}">
<tal:block metal:use-macro="here/Zuite_CommonTemplateForRenderjsUi/macros/wait_for_notification" />
</tal:block>
<tr>
<td>selectFrame</td>
<td>//div[@data-gadget-scope="editor"]//iframe</td>
<td></td>
</tr>
<tr>
<td>waitForElementPresent</td>
<td>css=div.monaco-editor.vs</td>
<td></td>
</tr>
<tr>
<td>storeEval</td>
<td>selenium.browserbot.getCurrentWindow().document.querySelector('div.monaco-editor.vs').getAttribute('data-uri')
</td>
<td>model-data-uri</td>
</tr>
<tr>
<td>assertEval</td>
<td>selenium.browserbot.getCurrentWindow().monaco.editor.getModel(storedVars['model-data-uri']).setValue(
"def&nbsp;foo():\n&nbsp;1+2\n")
</td>
<td>null</td>
</tr>
<tr>
<td>assertEval</td>
<td>selenium.browserbot.getCurrentWindow().monaco.editor.getEditors()[0].focus()
</td>
<td>null</td>
</tr>
<tr>
<td>assertEval</td>
<td>(function() {
selenium.browserbot.getCurrentWindow().monaco.editor.getEditors()[0].getAction('editor.action.formatDocument').run();
return "ok"})()
</td>
<td>ok</td>
</tr>
<tr>
<td>waitForEval</td>
<td>(function(){
return selenium.browserbot.getCurrentWindow().monaco.editor.getModel(storedVars['model-data-uri']).getValue()
/* code is formatted: indentation is fixed and spaces between "1 + 2" are added */
== ("def foo():\n" + " " + " " + "1 + 2\n")
})()</td>
<td>true</td>
</tr>
<tr>
<td>selectFrame</td>
<td>relative=top</td>
<td></td>
</tr>
</tbody>
</table>
</body>
</html>
\ No newline at end of file
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