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
Léo-Paul Géneau
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
Hide 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