[pgAdmin][RM4631] PgAdmin IV browser locks up when trying to undo an edit

Aditya Toshniwal <[email protected]>
Newsgroups gmane.comp.db.postgresql.pgadmin.devel
Message-ID <CAM9w-_mNhBmYfwC7OFkY5cxgfdfu8J=r4_wDKuFAyaQBNBX9fg@mail.gmail.com>
Hi Hackers,

Attached is the patch to:
1) Optimise code folding code for better performance.
2) Moved all editor related preferences like Font size, Tab size, Brace
matching, etc. from options to a new category "Editor" .
3) Added two new preferences - Plain editor mode (text/plain), Disable code
folding.
4) Codemirror has its own limitations and lags for long text line. Using
plain editor mode will help improve the speed as it does not have the
overhead of syntax highlighting and code folding. Optionally, one can
disable code folding in normal SQL mode.

Kindly review.
Thanks and Regards,
Aditya Toshniwal
Software Engineer | EnterpriseDB India | Pune
"Don't Complain about Heat, Plant a TREE"
RM4631.patch (application/octet-stream, 11.3 KB)
diff --git a/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js b/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js
index 595c7dc7..b5a3520c 100644
--- a/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js
+++ b/web/pgadmin/static/js/codemirror/addon/fold/pgadmin-sqlfoldcode.js
@@ -17,21 +17,30 @@
 })(function(CodeMirror) {
   'use strict';
 
-  CodeMirror.pgadminKeywordRangeFinder = function(cm, start, startTkn, endTkn) {
+  var pgadminKeywordRangeFinder = function(cm, start, tokenSet) {
     var line = start.line,
       lineText = cm.getLine(line);
     var at = lineText.length,
       startChar, tokenType;
+
+    let tokenSetNo = 0;
+    let startTkn = tokenSet[tokenSetNo].start,
+      endTkn = tokenSet[tokenSetNo].end;
     for (; at > 0;) {
       var found = lineText.lastIndexOf(startTkn, at);
       var startToken = startTkn;
       var endToken = endTkn;
 
       if (found < start.ch) {
-        found = lineText.lastIndexOf('[', at);
-        if (found < start.ch) break;
-        startToken = '[';
-        endToken = ']';
+        /* If the start token is not found then search for the next set of token */
+        tokenSetNo++;
+        if(tokenSetNo >= tokenSet.length) {
+          return undefined;
+        }
+        startTkn = tokenSet[tokenSetNo].start;
+        endTkn = tokenSet[tokenSetNo].end;
+        at = lineText.length;
+        continue;
       }
 
       tokenType = cm.getTokenAt(CodeMirror.Pos(line, found + 1)).type;
@@ -73,32 +82,13 @@
     };
   };
 
-  CodeMirror.pgadminBeginRangeFinder = function(cm, start) {
-    var startToken = 'BEGIN';
-    var endToken = 'END;';
-    var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
+  CodeMirror.registerHelper('fold', 'sql', function(cm, start) {
+    var fromToPos = pgadminKeywordRangeFinder(cm, start, [
+      {start: 'BEGIN', end:'END;'},
+      {start: 'IF', end:'END IF'},
+      {start: 'LOOP', end:'END LOOP'},
+      {start: 'CASE', end:'END CASE'},
+    ]);
     return fromToPos;
-  };
-
-  CodeMirror.pgadminIfRangeFinder = function(cm, start) {
-    var startToken = 'IF';
-    var endToken = 'END IF';
-    var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
-    return fromToPos;
-  };
-
-  CodeMirror.pgadminLoopRangeFinder = function(cm, start) {
-    var startToken = 'LOOP';
-    var endToken = 'END LOOP';
-    var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
-    return fromToPos;
-  };
-
-  CodeMirror.pgadminCaseRangeFinder = function(cm, start) {
-    var startToken = 'CASE';
-    var endToken = 'END CASE';
-    var fromToPos = CodeMirror.pgadminKeywordRangeFinder(cm, start, startToken, endToken);
-    return fromToPos;
-  };
-
+  });
 });
diff --git a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
index 491f0ca9..8e74e65d 100644
--- a/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
+++ b/web/pgadmin/static/js/sqleditor/query_tool_preferences.js
@@ -182,6 +182,15 @@ function updateUIPreferences(sqlEditor) {
   let sql_font_size = SqlEditorUtils.calcFontSize(preferences.sql_font_size);
   $(sqlEditor.query_tool_obj.getWrapperElement()).css('font-size', sql_font_size);
 
+  if(preferences.plain_editor_mode) {
+    sqlEditor.query_tool_obj.setOption('mode', 'text/plain');
+    /* Although not required, setting explicitly as codemirror will remove code folding only on next edit */
+    sqlEditor.query_tool_obj.setOption('foldGutter', false);
+  } else {
+    sqlEditor.query_tool_obj.setOption('mode', sqlEditor.handler.server_type === 'gpdb' ? 'text/x-gpsql' : 'text/x-pgsql');
+    sqlEditor.query_tool_obj.setOption('foldGutter', preferences.code_folding);
+  }
+  sqlEditor.query_tool_obj.setOption('foldGutter', preferences.code_folding);
   sqlEditor.query_tool_obj.setOption('indentWithTabs', !preferences.use_spaces);
   sqlEditor.query_tool_obj.setOption('indentUnit', preferences.tab_size);
   sqlEditor.query_tool_obj.setOption('tabSize', preferences.tab_size);
diff --git a/web/pgadmin/tools/debugger/static/js/direct.js b/web/pgadmin/tools/debugger/static/js/direct.js
index 123b9cf1..b7ee19c8 100644
--- a/web/pgadmin/tools/debugger/static/js/direct.js
+++ b/web/pgadmin/tools/debugger/static/js/direct.js
@@ -1792,14 +1792,7 @@ define([
           foldOptions: {
             widget: '\u2026',
           },
-          foldGutter: {
-            rangeFinder: CodeMirror.fold.combine(
-              CodeMirror.pgadminBeginRangeFinder,
-              CodeMirror.pgadminIfRangeFinder,
-              CodeMirror.pgadminLoopRangeFinder,
-              CodeMirror.pgadminCaseRangeFinder
-            ),
-          },
+          foldGutter: true,
           gutters: [
             'CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'breakpoints',
           ],
diff --git a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
index 905f8422..f6fbadf3 100644
--- a/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
+++ b/web/pgadmin/tools/sqleditor/static/js/sqleditor.js
@@ -356,14 +356,7 @@ define('tools.querytool', [
         foldOptions: {
           widget: '\u2026',
         },
-        foldGutter: {
-          rangeFinder: CodeMirror.fold.combine(
-            CodeMirror.pgadminBeginRangeFinder,
-            CodeMirror.pgadminIfRangeFinder,
-            CodeMirror.pgadminLoopRangeFinder,
-            CodeMirror.pgadminCaseRangeFinder
-          ),
-        },
+        foldGutter: true,
         gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter'],
         extraKeys: pgBrowser.editor_shortcut_keys,
         scrollbarStyle: 'simple',
diff --git a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
index 12e5f363..2dc88f71 100644
--- a/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
+++ b/web/pgadmin/tools/sqleditor/utils/query_tool_preferences.py
@@ -90,8 +90,62 @@ def RegisterQueryToolPreferences(self):
                          'Tool tabs.')
     )
 
+    self.show_prompt_save_query_changes = self.preference.register(
+        'Options', 'prompt_save_query_changes',
+        gettext("Prompt to save unsaved query changes?"), 'boolean', True,
+        category_label=gettext('Options'),
+        help_str=gettext(
+            'Specifies whether or not to prompt user to save unsaved '
+            'query on query tool exit.'
+        )
+    )
+
+    self.show_prompt_save_data_changes = self.preference.register(
+        'Options', 'prompt_save_data_changes',
+        gettext("Prompt to save unsaved data changes?"), 'boolean', True,
+        category_label=gettext('Options'),
+        help_str=gettext(
+            'Specifies whether or not to prompt user to save unsaved '
+            'data on data grid exit.'
+        )
+    )
+
+    self.show_prompt_commit_transaction = self.preference.register(
+        'Options', 'prompt_commit_transaction',
+        gettext("Prompt to commit/rollback active transactions?"), 'boolean',
+        True,
+        category_label=gettext('Options'),
+        help_str=gettext(
+            'Specifies whether or not to prompt user to commit or rollback '
+            'an active transaction on Query Tool exit.'
+        )
+    )
+
+    self.sql_font_size = self.preference.register(
+        'Editor', 'plain_editor_mode',
+        gettext("Plain editor mode"), 'boolean', False,
+        category_label=gettext('Editor'),
+        help_str=gettext(
+            'When set to True, keywords won''t be highlighted and code '
+            'folding will be disabled.'
+            'Tip: Disabling will improve editor performance.'
+        )
+    )
+
     self.sql_font_size = self.preference.register(
-        'Options', 'sql_font_size',
+        'Editor', 'code_folding',
+        gettext("Code folding"), 'boolean', True,
+        category_label=gettext('Editor'),
+        help_str=gettext(
+            'Specifies if code folding needs to be disabled. '
+            'Note that, in plain editor mode, this will have no '
+            'effect as code folding is disabled in that mode.'
+            'Tip: Disabling will improve editor performance.'
+        )
+    )
+
+    self.sql_font_size = self.preference.register(
+        'Editor', 'sql_font_size',
         gettext("Font size"), 'numeric', '1',
         min_val=0.1,
         max_val=10,
@@ -106,7 +160,7 @@ def RegisterQueryToolPreferences(self):
     )
 
     self.tab_size = self.preference.register(
-        'Options', 'tab_size',
+        'Editor', 'tab_size',
         gettext("Tab size"), 'integer', 4,
         min_val=2,
         max_val=8,
@@ -117,7 +171,7 @@ def RegisterQueryToolPreferences(self):
     )
 
     self.use_spaces = self.preference.register(
-        'Options', 'use_spaces',
+        'Editor', 'use_spaces',
         gettext("Use spaces?"), 'boolean', False,
         category_label=gettext('Options'),
         help_str=gettext(
@@ -127,7 +181,7 @@ def RegisterQueryToolPreferences(self):
     )
 
     self.wrap_code = self.preference.register(
-        'Options', 'wrap_code',
+        'Editor', 'wrap_code',
         gettext("Line wrapping?"), 'boolean', False,
         category_label=gettext('Options'),
         help_str=gettext(
@@ -136,7 +190,7 @@ def RegisterQueryToolPreferences(self):
     )
 
     self.insert_pair_brackets = self.preference.register(
-        'Options', 'insert_pair_brackets',
+        'Editor', 'insert_pair_brackets',
         gettext("Insert bracket pairs?"), 'boolean', True,
         category_label=gettext('Options'),
         help_str=gettext(
@@ -146,7 +200,7 @@ def RegisterQueryToolPreferences(self):
     )
 
     self.brace_matching = self.preference.register(
-        'Options', 'brace_matching',
+        'Editor', 'brace_matching',
         gettext("Brace matching?"), 'boolean', True,
         category_label=gettext('Options'),
         help_str=gettext(
@@ -155,37 +209,6 @@ def RegisterQueryToolPreferences(self):
         )
     )
 
-    self.show_prompt_save_query_changes = self.preference.register(
-        'Options', 'prompt_save_query_changes',
-        gettext("Prompt to save unsaved query changes?"), 'boolean', True,
-        category_label=gettext('Options'),
-        help_str=gettext(
-            'Specifies whether or not to prompt user to save unsaved '
-            'query on query tool exit.'
-        )
-    )
-
-    self.show_prompt_save_data_changes = self.preference.register(
-        'Options', 'prompt_save_data_changes',
-        gettext("Prompt to save unsaved data changes?"), 'boolean', True,
-        category_label=gettext('Options'),
-        help_str=gettext(
-            'Specifies whether or not to prompt user to save unsaved '
-            'data on data grid exit.'
-        )
-    )
-
-    self.show_prompt_commit_transaction = self.preference.register(
-        'Options', 'prompt_commit_transaction',
-        gettext("Prompt to commit/rollback active transactions?"), 'boolean',
-        True,
-        category_label=gettext('Options'),
-        help_str=gettext(
-            'Specifies whether or not to prompt user to commit or rollback '
-            'an active transaction on Query Tool exit.'
-        )
-    )
-
     self.csv_quoting = self.preference.register(
         'CSV_output', 'csv_quoting',
         gettext("CSV quoting"), 'options', 'strings',
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.