Repository: spyder-ide/spyder

Issue:
Restore code folding for large files
In order to do that we need to do the following:

* Only draw and update the folding panel when the user moves the cursor over it.
* Only draw the visible region in the editor (although that's probably not so necessary if we draw the panel on demand).

@andfoy, please take a look at this one.


Discussion:

Resolving patch:
diff --git a/spyder/plugins/editor/panels/codefolding.py b/spyder/plugins/editor/panels/codefolding.py
--- a/spyder/plugins/editor/panels/codefolding.py
+++ b/spyder/plugins/editor/panels/codefolding.py
@@ -168,6 +168,8 @@ def __init__(self, highlight_caret_scope=False):
         self.action_collapse_all = None
         self.action_expand_all = None
         self._original_background = None
+        self._display_folding = False
+        self._key_pressed = False
         self._highlight_runner = DelayJobRunner(delay=250)
         self.folding_regions = {}
         self.folding_status = {}
@@ -243,11 +245,54 @@ def sizeHint(self):
             size_hint.setWidth(16)
         return size_hint
 
+    def _draw_collapsed_indicator(self, line_number, top_position, block,
+                                  painter, mouse_hover=False):
+        if line_number in self.folding_regions:
+            collapsed = self.folding_status[line_number]
+            line_end = self.folding_regions[line_number]
+            mouse_over = self._mouse_over_line == line_number
+            if not mouse_hover:
+                self._draw_fold_indicator(
+                    top_position, mouse_over, collapsed, painter)
+            if collapsed:
+                if mouse_hover:
+                    self._draw_fold_indicator(
+                        top_position, mouse_over, collapsed, painter)
+                # check if the block already has a decoration,
+                # it might have been folded by the parent
+                # editor/document in the case of cloned editor
+                for deco in self._block_decos:
+                    if deco.block == block:
+                        # no need to add a deco, just go to the
+                        # next block
+                        break
+                else:
+                    self._add_fold_decoration(block, line_end)
+            elif not mouse_hover:
+                for deco in self._block_decos:
+                    # check if the block decoration has been removed, it
+                    # might have been unfolded by the parent
+                    # editor/document in the case of cloned editor
+                    if deco.block == block:
+                        # remove it and
+                        self._block_decos.remove(deco)
+                        self.editor.decorations.remove(deco)
+                        del deco
+                        break
+
     def paintEvent(self, event):
         # Paints the fold indicators and the possible fold region background
         # on the folding panel.
         super(FoldingPanel, self).paintEvent(event)
         painter = QPainter(self)
+        if not self._display_folding and not self._key_pressed:
+            if any(self.folding_status.values()):
+                for info in self.editor.visible_blocks:
+                    top_position, line_number, block = info
+                    self._draw_collapsed_indicator(
+                        line_number, top_position, block,
+                        painter, mouse_hover=True)
+            return
         # Draw background over the selected non collapsed fold region
         if self._mouse_over_line is not None:
             block = self.editor.document().findBlockByNumber(
@@ -263,33 +308,8 @@ def paintEvent(self, event):
                 pass
         # Draw fold triggers
         for top_position, line_number, block in self.editor.visible_blocks:
-            if line_number in self.folding_regions:
-                collapsed = self.folding_status[line_number]
-                line_end = self.folding_regions[line_number]
-                mouse_over = self._mouse_over_line == line_number
-                self._draw_fold_indicator(
-                    top_position, mouse_over, collapsed, painter)
-                if collapsed:
-                    # check if the block already has a decoration, it might
-                    # have been folded by the parent editor/document in the
-                    # case of cloned editor
-                    for deco in self._block_decos:
-                        if deco.block == block:
-                            # no need to add a deco, just go to the next block
-                            break
-                    else:
-                        self._add_fold_decoration(block, line_end)
-                else:
-                    for deco in self._block_decos:
-                        # check if the block decoration has been removed, it
-                        # might have been unfolded by the parent
-                        # editor/document in the case of cloned editor
-                        if deco.block == block:
-                            # remove it and
-                            self._block_decos.remove(deco)
-                            self.editor.decorations.remove(deco)
-                            del deco
-                            break
+            self._draw_collapsed_indicator(
+                line_number, top_position, block, painter, mouse_hover=False)
 
     def _draw_fold_region_background(self, block, painter):
         """
@@ -503,6 +523,10 @@ def mouseMoveEvent(self, event):
                 QApplication.restoreOverrideCursor()
             self.repaint()
 
+    def enterEvent(self, event):
+        self._display_folding = True
+        self.repaint()
+
     def leaveEvent(self, event):
         """
         Removes scope decorations and background from the editor and the panel
@@ -520,6 +544,7 @@ def leaveEvent(self, event):
             self._block_nbr = -1
             self._highlight_caret_scope()
         self.editor.repaint()
+        self._display_folding = False
 
     def _add_fold_decoration(self, block, end_line):
         """
@@ -631,6 +656,7 @@ def _on_key_pressed(self, event):
                                          Qt.Key_Delete]
         if event.text() or delete_request:
             cursor = self.editor.textCursor()
+            self._key_pressed = True
             if cursor.hasSelection():
                 # change selection to encompass the whole scope.
                 positions_to_check = cursor.selectionStart(), cursor.selectionEnd()
@@ -658,6 +684,7 @@ def _on_key_pressed(self, event):
                         self.editor.setTextCursor(tc)
                         self.folding_regions.pop(start_line)
                         self.folding_status.pop(start_line)
+            self._key_pressed = False
 
     @staticmethod
     def _show_previous_blank_lines(block):
diff --git a/spyder/plugins/editor/widgets/codeeditor.py b/spyder/plugins/editor/widgets/codeeditor.py
--- a/spyder/plugins/editor/widgets/codeeditor.py
+++ b/spyder/plugins/editor/widgets/codeeditor.py
@@ -1472,32 +1472,6 @@ def compute_whitespace(line):
     @request(method=LSPRequestTypes.DOCUMENT_FOLDING_RANGE)
     def request_folding(self):
         """Request folding."""
-        total_lines = self.get_line_count()
-        if total_lines > 2000 and self.code_folding:
-            warn = CONF.get('editor', 'show_code_folding_warning')
-            warn_str = _(
-                "One of the files in the editor or the file you are trying "
-                "to open contains more than 2000 lines.<br><br>"
-                "Code folding and indent guidelines will be disabled for "
-                "this kind of files in order to prevent performance "
-                "degradation."
-            )
-            if warn:
-                box = MessageCheckBox(
-                    icon=QMessageBox.Warning, parent=self)
-                box.setWindowTitle(_('File too long'))
-                box.set_checkbox_text(_("Don't show again."))
-                box.setStandardButtons(QMessageBox.Ok)
-                box.set_checked(False)
-                box.set_check_visible(True)
-                box.setText(warn_str)
-                box.exec_()
-
-                CONF.set('editor', 'show_code_folding_warning',
-                         not box.is_checked())
-            self.toggle_code_folding(False)
-            self.toggle_identation_guides(False)
-
         if not self.folding_supported or not self.code_folding:
             return
         params = {'file': self.filename}


Test patch: