Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
erp5
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Levin Zimmermann
erp5
Commits
9e0c691e
Commit
9e0c691e
authored
Jun 04, 2018
by
Jérome Perrin
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
erp5: enable monaco as a code editor
parent
f6d78579
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
280 additions
and
4 deletions
+280
-4
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.xml
...portal_skins/erp5_monaco_editor/monaco_editor_support.xml
+58
-0
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
...portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
+190
-0
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorList.py
.../erp5_core/Preference_getAvailableSourceCodeEditorList.py
+3
-0
product/ERP5Form/EditorField.py
product/ERP5Form/EditorField.py
+18
-0
product/ERP5Type/patches/AceEditorZMI.py
product/ERP5Type/patches/AceEditorZMI.py
+11
-4
No files found.
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.xml
0 → 100644
View file @
9e0c691e
<?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>
monaco_editor_support
</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></unicode>
</value>
</item>
</dictionary>
</pickle>
</record>
</ZopeData>
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.zpt
0 → 100644
View file @
9e0c691e
<tal:block tal:condition="options/field_id | nothing">
<tal:comment tal:replace="nothing">When rendered as an ERP5 field, we need to add a textarea and adjust CSS.
</tal:comment>
<textarea tal:attributes="id options/field_id;
name options/field_id"
style="display:none"
tal:content="options/content">
</textarea>
<style>
/* Override some conflicting default erp5.css styles */
/* - font size and indentation rules */
span {
font-family: unset !important;
}
div#monaco-container * {
font-family: Menlo, Monaco, "Courier New", monospace;
font-size: unset !important;
}
/* - selected text highlight */
.monaco-editor .view-lines {
background-color: transparent !important;
}
/* - popup menu */
.monaco-action-bar .action-label.disabled {
height: 0px;
}
.monaco-menu li.action-item a.action-label {
font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback" !important;
}
/* - Command palette (FIXME: background color inherits ERP5 page background color) */
.quick-open-entry * {
font-family: "Segoe WPC", "Segoe UI", ".SFNSDisplay-Light", "SFUIText-Light", "HelveticaNeue-Light", sans-serif, "Droid Sans Fallback" !important;
}
</style>
</tal:block>
<div id="monaco-container" style="width:100%;height:800px;border:1px solid grey;"></div>
<script tal:content='python: "var portal_url=" + modules["json"].dumps(options.get("portal_url"))'></script>
<script tal:content='python: "var field_id=" + modules["json"].dumps(options.get("field_id"))'></script>
<script tal:content='python: "var mode=" + modules["json"].dumps(options["mode"])'></script>
<script tal:content='python: "var textarea_selector=" + modules["json"].dumps(options.get("textarea_selector"))'>
</script>
<script tal:content='python: "var bound_names=" + modules["json"].dumps(options.get("bound_names"))'></script>
<script
tal:content='python: "window.monacoEditorWebPackResourceBaseUrl = " + modules["json"].dumps(options["portal_url"]) + " + \"/monaco-editor/\""'>
</script>
<script charset="utf-8">
/* we need to defer import for the monacoEditorWebPackResourceBaseUrl trick to work as expected in ZMI */
var $script = document.createElement("script");
$script.src =
window.monacoEditorWebPackResourceBaseUrl + "/monaco-editor/app.bundle.min.js";
document.head.appendChild($script);
$script.onload = function() {
var $textarea =
document.querySelector(textarea_selector) ||
document.getElementById(field_id);
if (textarea_selector) {
/* ZMI mode */
/* create a div instead of the default textarea */
var $monacoContainer = document.getElementById("monaco-container");
$monacoContainer.parentNode.removeChild($monacoContainer);
$textarea.parentNode.appendChild($monacoContainer);
$monacoContainer.style.width = $textarea.parentNode.offsetWidth - 10 + "px";
$monacoContainer.style.height = $textarea.parentNode.offsetHeight + "px";
$textarea.style.display = "none";
function saveDocument() {
var $saveButton = document.querySelector('input[value="Save Changes"]');
$saveButton.click();
return false;
}
} else {
/* ERP5 editor field mode */
/* all ERP5 field have a .title that shows a popup, we don't want this popup on this editor */
$textarea.parentNode.title = "";
function saveDocument() {
clickSaveButton("Base_edit");
document.getElementById("main_form").submit();
}
}
// this is codemorrir only
if (mode === "htmlmixed") {
mode = "html";
}
var editor = monaco.editor.create(
document.getElementById("monaco-container"),
{
value: $textarea.value,
language: mode,
/* because Alt+Click is LeftClick on ChromeOS */
multiCursorModifier: "ctrlCmd",
autoIndent: true
}
);
if (mode == "python") {
editor.getModel().updateOptions({ tabSize: 2 });
}
if (mode === "html") {
monaco.languages.html.htmlDefaults.options.format.tabSize = 2;
monaco.languages.html.htmlDefaults.options.format.insertSpaces = true;
}
var timeout = null;
function checkPythonSourceCode() {
const data = new FormData();
const checker_parameters = {
code: editor.getValue()
};
// ZMI python scripts pass extra parameters to linter
if (bound_names) {
checker_parameters["bound_names"] = JSON.parse(bound_names);
checker_parameters["params"] = document.querySelector(
'input[name="params"]'
).value;
}
data.append("data", JSON.stringify(checker_parameters));
fetch(portal_url + "/ERP5Site_checkPythonSourceCodeAsJSON", {
method: "POST",
body: data
})
.then(response => response.json())
.then(data => {
monaco.editor.setModelMarkers(
editor.getModel(),
"pylint",
data["annotations"].map(annotation => {
return {
startLineNumber: annotation.row + 1,
endLineNumber: annotation.row + 1,
startColumn: annotation.col,
endColumn: Infinity,
message: annotation.text,
severity:
annotation.type === "error"
? monaco.MarkerSeverity.Error
: monaco.MarkerSeverity.Warning
};
})
);
timeout = null;
});
}
editor.getModel().onDidChangeContent(event => {
$textarea.value = editor.getValue();
changed = true; /* global variable used in erp5.js for onbeforeunload event */
if (mode == "python") {
// debounced `checkPythonSourceCode`
if (timeout) {
clearTimeout(timeout);
}
timeout = setTimeout(checkPythonSourceCode, 300);
}
});
if (mode === "python") {
// Perform a first check when loading document.
checkPythonSourceCode();
}
editor.addAction({
id: "save",
label: "Save",
keybindings: [monaco.KeyMod.CtrlCmd | monaco.KeyCode.KEY_S],
precondition: null,
keybindingContext: null,
contextMenuGroupId: "navigation",
contextMenuOrder: 1.5,
run: function(ed) {
return saveDocument();
}
});
};
</script>
\ No newline at end of file
product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorList.py
View file @
9e0c691e
...
...
@@ -6,4 +6,7 @@ if getattr(context.portal_skins, "erp5_ace_editor", None) is not None:
if
getattr
(
context
.
portal_skins
,
"erp5_code_mirror"
,
None
)
is
not
None
:
editor_list
.
append
((
"Code Mirror"
,
"codemirror"
))
if
getattr
(
context
.
portal_skins
,
"erp5_monaco_editor"
,
None
)
is
not
None
:
editor_list
.
append
((
"Monaco Editor"
,
"monaco"
))
return
editor_list
product/ERP5Form/EditorField.py
View file @
9e0c691e
...
...
@@ -98,6 +98,24 @@ class EditorWidget(Widget.TextAreaWidget):
return
ace_editor_support
.
pt_render
(
extra_context
=
{
'field'
:
field
,
'content'
:
value
,
'id'
:
key
})
elif
text_editor
==
'monaco'
:
monaco_editor_support
=
getattr
(
here
,
'monaco_editor_support'
,
None
)
if
monaco_editor_support
is
not
None
:
mode
=
"python"
portal_type
=
here
.
getPortalType
()
if
portal_type
==
"Web Page"
:
mode
=
"htmlmixed"
elif
portal_type
==
"Web Script"
:
mode
=
"javascript"
elif
portal_type
==
"Web Style"
:
mode
=
"css"
site_root
=
here
.
getWebSiteValue
()
or
here
.
getPortalObject
()
return
monaco_editor_support
(
field
=
field
,
content
=
value
,
field_id
=
key
,
portal_url
=
site_root
.
absolute_url
(),
mode
=
mode
)
elif
text_editor
==
'codemirror'
:
code_mirror_support
=
getattr
(
here
,
'code_mirror_support'
,
None
)
if
code_mirror_support
is
not
None
:
...
...
product/ERP5Type/patches/AceEditorZMI.py
View file @
9e0c691e
...
...
@@ -19,9 +19,6 @@ def manage_page_footer(self):
except
:
editor
=
None
if
editor
not
in
(
'ace'
,
'codemirror'
):
return
default
# REQUEST['PUBLISHED'] can be the form in the acquisition context of the
# document, or a method bound to the document (after a POST it is a bound method)
published
=
self
.
REQUEST
.
get
(
'PUBLISHED'
)
...
...
@@ -96,7 +93,15 @@ def manage_page_footer(self):
mode
=
mode
,
keymap
=
keymap
,
portal_type
=
portal_type
))
else
:
elif
editor
==
'monaco'
and
getattr
(
portal
,
'monaco_editor_support'
,
None
)
is
not
None
:
return
'''%s
</body>
</html>'''
%
(
portal
.
monaco_editor_support
(
textarea_selector
=
textarea_selector
,
portal_url
=
portal_url
,
bound_names
=
bound_names
,
mode
=
mode
).
encode
(
'utf-8'
))
elif
editor
==
'ace'
:
return
'''
<script type="text/javascript" src="%(portal_url)s/jquery/core/jquery.min.js"></script>
<script type="text/javascript" src="%(portal_url)s/ace/ace.js"></script>
...
...
@@ -183,4 +188,6 @@ $(document).ready(function() {
</body>
</html>'''
%
locals
()
return
default
Navigation
.
manage_page_footer
=
manage_page_footer
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment