From 90c7c7aac6074fe1f03aedf266388244794eb99f Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Thu, 15 Feb 2018 19:16:04 +0900
Subject: [PATCH] erp5_code_mirror: Add support for alternative keymaps (Emacs
 and Vim).

This can be chosen through Preference => "User Interface" tab. CodeMirror
default keymap is used by default as until now.
---
 .../erp5_code_mirror/code_mirror_support.txt  |   8 +
 ..._getAvailableSourceCodeEditorKeymapList.py |   6 +
 ...getAvailableSourceCodeEditorKeymapList.xml |  62 ++++
 .../erp5_core/Preference_viewHtmlStyle.xml    |   1 +
 ...my_preferred_source_code_editor_keymap.xml | 270 ++++++++++++++++++
 ...red_source_code_editor_keymap_property.xml |  40 +++
 product/ERP5Form/EditorField.py               |   3 +-
 product/ERP5Type/patches/AceEditorZMI.py      |   4 +-
 8 files changed, 392 insertions(+), 2 deletions(-)
 create mode 100644 product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.py
 create mode 100644 product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.xml
 create mode 100644 product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle/my_preferred_source_code_editor_keymap.xml
 create mode 100644 product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/Preference/preferred_source_code_editor_keymap_property.xml

diff --git a/bt5/erp5_code_mirror/SkinTemplateItem/portal_skins/erp5_code_mirror/code_mirror_support.txt b/bt5/erp5_code_mirror/SkinTemplateItem/portal_skins/erp5_code_mirror/code_mirror_support.txt
index b0c43627ed..1e3b3ef724 100644
--- a/bt5/erp5_code_mirror/SkinTemplateItem/portal_skins/erp5_code_mirror/code_mirror_support.txt
+++ b/bt5/erp5_code_mirror/SkinTemplateItem/portal_skins/erp5_code_mirror/code_mirror_support.txt
@@ -38,6 +38,13 @@
 <script type="text/javascript" src="&dtml-portal_url;/diff_match_patch/javascript/diff_match_patch_uncompressed.js"></script>
 <script type="text/javascript" src="&dtml-portal_url;/codemirror/addon/merge/merge.js"></script>
 
+<!-- Keymaps -->
+<dtml-if expr="keymap == 'emacs'">
+<script type="text/javascript" src="&dtml-portal_url;/codemirror/keymap/emacs.js"></script>
+<dtml-elif expr="keymap == 'vim'">
+<script type="text/javascript" src="&dtml-portal_url;/codemirror/keymap/vim.js"></script>
+</dtml-if>
+
 <!-- Linter -->
 <link rel="stylesheet" href="&dtml-portal_url;/codemirror/addon/lint/lint.css">
 <script type="text/javascript" src="&dtml-portal_url;/codemirror/addon/lint/lint.js"></script>
@@ -484,6 +491,7 @@
  var cm = CodeMirror.fromTextArea(
    getTextareaField()[0],
    {mode: mode,
+    keyMap: "&dtml-keymap;",
     lineNumbers: true,
     styleActiveLine: true,
     showTrailingSpace: true,
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.py b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.py
new file mode 100644
index 0000000000..6400a92d0b
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.py
@@ -0,0 +1,6 @@
+editor_list = [("Default", "default")]
+
+if context.getPreferredSourceCodeEditor() == 'codemirror':
+  editor_list.extend([("Emacs", "emacs"), ("Vim", "vim")])
+
+return editor_list
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.xml
new file mode 100644
index 0000000000..fafd51bb3c
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_getAvailableSourceCodeEditorKeymapList.xml
@@ -0,0 +1,62 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>Script_magic</string> </key>
+            <value> <int>3</int> </value>
+        </item>
+        <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_container</string> </key>
+                                <value> <string>container</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_context</string> </key>
+                                <value> <string>context</string> </value>
+                            </item>
+                            <item>
+                                <key> <string>name_m_self</string> </key>
+                                <value> <string>script</string> </value>
+                            </item>
+                            <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>_params</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>Preference_getAvailableSourceCodeEditorKeymapList</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle.xml
index 1c4731d6f7..82fe72709f 100644
--- a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle.xml
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle.xml
@@ -69,6 +69,7 @@
                       <list>
                         <string>my_preferred_text_editor</string>
                         <string>my_preferred_source_code_editor</string>
+                        <string>my_preferred_source_code_editor_keymap</string>
                         <string>my_preferred_text_format</string>
                       </list>
                     </value>
diff --git a/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle/my_preferred_source_code_editor_keymap.xml b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle/my_preferred_source_code_editor_keymap.xml
new file mode 100644
index 0000000000..eb8b0af006
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_core/SkinTemplateItem/portal_skins/erp5_core/Preference_viewHtmlStyle/my_preferred_source_code_editor_keymap.xml
@@ -0,0 +1,270 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="RadioField" module="Products.Formulator.StandardFields"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>my_preferred_source_code_editor_keymap</string> </value>
+        </item>
+        <item>
+            <key> <string>message_values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>external_validator_failed</string> </key>
+                    <value> <string>The input failed the external validator.</string> </value>
+                </item>
+                <item>
+                    <key> <string>required_not_found</string> </key>
+                    <value> <string>Input is required but no input given.</string> </value>
+                </item>
+                <item>
+                    <key> <string>unknown_selection</string> </key>
+                    <value> <string>You selected an item that was not in the list.</string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>overrides</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>alternate_name</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>css_class</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>default</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>description</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>editable</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>enabled</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>external_validator</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>extra_item</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>first_item</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>hidden</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>items</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>orientation</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>required</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>title</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>unicode</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>whitespace_preserve</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>tales</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>alternate_name</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>css_class</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>default</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>description</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>editable</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>enabled</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>external_validator</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>extra_item</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>first_item</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>hidden</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>items</string> </key>
+                    <value>
+                      <persistent> <string encoding="base64">AAAAAAAAAAI=</string> </persistent>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>orientation</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>required</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>title</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>unicode</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>whitespace_preserve</string> </key>
+                    <value> <string></string> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+        <item>
+            <key> <string>values</string> </key>
+            <value>
+              <dictionary>
+                <item>
+                    <key> <string>alternate_name</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>css_class</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>default</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>description</string> </key>
+                    <value> <string>The source code editor keymap used by default</string> </value>
+                </item>
+                <item>
+                    <key> <string>editable</string> </key>
+                    <value> <int>1</int> </value>
+                </item>
+                <item>
+                    <key> <string>enabled</string> </key>
+                    <value> <int>1</int> </value>
+                </item>
+                <item>
+                    <key> <string>external_validator</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>extra_item</string> </key>
+                    <value> <string></string> </value>
+                </item>
+                <item>
+                    <key> <string>first_item</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+                <item>
+                    <key> <string>hidden</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+                <item>
+                    <key> <string>items</string> </key>
+                    <value>
+                      <list>
+                        <tuple>
+                          <string>Default</string>
+                          <string>default</string>
+                        </tuple>
+                      </list>
+                    </value>
+                </item>
+                <item>
+                    <key> <string>orientation</string> </key>
+                    <value> <string>horizontal</string> </value>
+                </item>
+                <item>
+                    <key> <string>required</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+                <item>
+                    <key> <string>title</string> </key>
+                    <value> <string>Source Code Editor Keymap</string> </value>
+                </item>
+                <item>
+                    <key> <string>unicode</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+                <item>
+                    <key> <string>whitespace_preserve</string> </key>
+                    <value> <int>0</int> </value>
+                </item>
+              </dictionary>
+            </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+  <record id="2" aka="AAAAAAAAAAI=">
+    <pickle>
+      <global name="TALESMethod" module="Products.Formulator.TALESField"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_text</string> </key>
+            <value> <string>here/Preference_getAvailableSourceCodeEditorKeymapList</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/Preference/preferred_source_code_editor_keymap_property.xml b/product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/Preference/preferred_source_code_editor_keymap_property.xml
new file mode 100644
index 0000000000..c558531a4c
--- /dev/null
+++ b/product/ERP5/bootstrap/erp5_property_sheets/PropertySheetTemplateItem/portal_property_sheets/Preference/preferred_source_code_editor_keymap_property.xml
@@ -0,0 +1,40 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="Standard Property" module="erp5.portal_type"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>categories</string> </key>
+            <value>
+              <tuple>
+                <string>elementary_type/selection</string>
+              </tuple>
+            </value>
+        </item>
+        <item>
+            <key> <string>description</string> </key>
+            <value> <string>Preferred Source Code Editor Keymap</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>preferred_source_code_editor_keymap_property</string> </value>
+        </item>
+        <item>
+            <key> <string>portal_type</string> </key>
+            <value> <string>Standard Property</string> </value>
+        </item>
+        <item>
+            <key> <string>preference</string> </key>
+            <value> <int>1</int> </value>
+        </item>
+        <item>
+            <key> <string>property_default</string> </key>
+            <value> <string>python: \'default\'</string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/product/ERP5Form/EditorField.py b/product/ERP5Form/EditorField.py
index 4ff282c8a9..38fd40277d 100644
--- a/product/ERP5Form/EditorField.py
+++ b/product/ERP5Form/EditorField.py
@@ -114,7 +114,8 @@ class EditorWidget(Widget.TextAreaWidget):
                                    content=value,
                                    field_id=key,
                                    portal_url=site_root.absolute_url(),
-                                   mode=mode)
+                                   mode=mode,
+                                   keymap=site_root.portal_preferences.getPreferredSourceCodeEditorKeymap())
     elif text_editor != 'text_area':
       return here.fckeditor_wysiwyg_support.pt_render(
            extra_context= {
diff --git a/product/ERP5Type/patches/AceEditorZMI.py b/product/ERP5Type/patches/AceEditorZMI.py
index e38b08525a..c525f3a63b 100644
--- a/product/ERP5Type/patches/AceEditorZMI.py
+++ b/product/ERP5Type/patches/AceEditorZMI.py
@@ -84,6 +84,7 @@ def manage_page_footer(self):
     return default
 
   if editor == 'codemirror' and getattr(portal, 'code_mirror_support', None) is not None:
+    keymap = portal.portal_preferences.getPreferredSourceCodeEditorKeymap()
     return '''<script type="text/javascript" src="%s/jquery/core/jquery.min.js"></script>
               %s
               </body>
@@ -91,7 +92,8 @@ def manage_page_footer(self):
                           portal.code_mirror_support(textarea_selector=textarea_selector,
                                                      portal_url=portal_url,
                                                      bound_names=bound_names,
-                                                     mode=mode))
+                                                     mode=mode,
+                                                     keymap=keymap))
   else:
     return '''
 <script type="text/javascript" src="%(portal_url)s/jquery/core/jquery.min.js"></script>
-- 
2.30.9