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
Labels
Merge Requests
7
Merge Requests
7
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Jobs
Commits
Open sidebar
Jérome Perrin
erp5
Commits
d881b1e6
Commit
d881b1e6
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
1ee3aced
Changes
5
Show whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
273 additions
and
4 deletions
+273
-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
+181
-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
+13
-4
No files found.
bt5/erp5_monaco_editor/SkinTemplateItem/portal_skins/erp5_monaco_editor/monaco_editor_support.xml
0 → 100644
View file @
d881b1e6
<?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 @
d881b1e6
<div id="monaco-container" style="width:100%;height:800px;border:1px solid grey;"></div>
<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>
<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>
/* 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();
}
}
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.model.updateOptions({ tabSize: 2 });
monaco.languages.setLanguageConfiguration("python", {
indentationRules: {
increaseIndentPattern: /^.*:$/
// decreaseIndentPattern: /^$/
// TODO: unindent rule
}
});
}
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.model,
"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.Severity.Error
: monaco.Severity.Warning
};
})
);
timeout = null;
});
}
editor.model.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 @
d881b1e6
...
@@ -6,4 +6,7 @@ if getattr(context.portal_skins, "erp5_ace_editor", None) is not None:
...
@@ -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
:
if
getattr
(
context
.
portal_skins
,
"erp5_code_mirror"
,
None
)
is
not
None
:
editor_list
.
append
((
"Code Mirror"
,
"codemirror"
))
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
return
editor_list
product/ERP5Form/EditorField.py
View file @
d881b1e6
...
@@ -119,6 +119,24 @@ class EditorWidget(Widget.TextAreaWidget):
...
@@ -119,6 +119,24 @@ class EditorWidget(Widget.TextAreaWidget):
return
ace_editor_support
.
pt_render
(
extra_context
=
{
'field'
:
field
,
return
ace_editor_support
.
pt_render
(
extra_context
=
{
'field'
:
field
,
'content'
:
value
,
'content'
:
value
,
'id'
:
key
})
'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'
:
elif
text_editor
==
'codemirror'
:
code_mirror_support
=
getattr
(
here
,
'code_mirror_support'
,
None
)
code_mirror_support
=
getattr
(
here
,
'code_mirror_support'
,
None
)
if
code_mirror_support
is
not
None
:
if
code_mirror_support
is
not
None
:
...
...
product/ERP5Type/patches/AceEditorZMI.py
View file @
d881b1e6
...
@@ -19,9 +19,6 @@ def manage_page_footer(self):
...
@@ -19,9 +19,6 @@ def manage_page_footer(self):
except
:
except
:
editor
=
None
editor
=
None
if
editor
not
in
(
'ace'
,
'codemirror'
):
return
default
# REQUEST['PUBLISHED'] can be the form in the acquisition context of the
# 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)
# document, or a method bound to the document (after a POST it is a bound method)
published
=
self
.
REQUEST
.
get
(
'PUBLISHED'
)
published
=
self
.
REQUEST
.
get
(
'PUBLISHED'
)
...
@@ -92,7 +89,17 @@ def manage_page_footer(self):
...
@@ -92,7 +89,17 @@ def manage_page_footer(self):
portal_url
=
portal_url
,
portal_url
=
portal_url
,
bound_names
=
bound_names
,
bound_names
=
bound_names
,
mode
=
mode
))
mode
=
mode
))
else
:
if
editor
==
'monaco'
and
getattr
(
portal
,
'monaco_editor_support'
,
None
)
is
not
None
:
return
'''<script type="text/javascript" src="%s/jquery/core/jquery.min.js"></script>
%s
</body>
</html>'''
%
(
portal_url
,
portal
.
monaco_editor_support
(
textarea_selector
=
textarea_selector
,
portal_url
=
portal_url
,
bound_names
=
bound_names
,
mode
=
mode
))
elif
editor
==
'ace'
:
return
'''
return
'''
<script type="text/javascript" src="%(portal_url)s/jquery/core/jquery.min.js"></script>
<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>
<script type="text/javascript" src="%(portal_url)s/ace/ace.js"></script>
...
@@ -178,4 +185,6 @@ $(document).ready(function() {
...
@@ -178,4 +185,6 @@ $(document).ready(function() {
</body>
</body>
</html>'''
%
locals
()
</html>'''
%
locals
()
return
default
Navigation
.
manage_page_footer
=
manage_page_footer
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