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
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