src/eric7/QScintilla/Editor.py

branch
eric7
changeset 10376
4146ac0fd307
parent 10373
093dcebe5ecb
child 10377
9f6ffcd1db54
--- a/src/eric7/QScintilla/Editor.py	Mon Dec 04 10:33:58 2023 +0100
+++ b/src/eric7/QScintilla/Editor.py	Mon Dec 04 15:21:07 2023 +0100
@@ -4185,9 +4185,24 @@
             return line.strip().startswith(commentStr)
 
     @pyqtSlot()
-    def toggleCommentBlock(self):
-        """
-        Public slot to toggle the comment of a block.
+    def toggleComment(self):
+        """
+        Public slot to toggle a block or stream comment.
+
+        If the lexer supports a block comment, that is used for toggling the
+        comment. Otherwise a stream comment is used if that is supported. If
+        none of these are supported, the request is ignored silently.
+        """
+        if self.lexer_ is not None:
+            if self.lexer_.canBlockComment():
+                self.__toggleBlockComment()
+            elif self.lexer_.canStreamComment():
+                self.__toggleStreamComment()
+
+    @pyqtSlot()
+    def __toggleBlockComment(self):
+        """
+        Private slot to toggle the comment of a block.
 
         If the editor contains selected text and the start line is not commented, it
         will be commented. Otherwise the selection will be un-commented. In case there
@@ -4233,9 +4248,9 @@
             self.setCursorPosition(line, index - len(commentStr))
 
     @pyqtSlot()
-    def commentLine(self):
-        """
-        Public slot to comment the current line.
+    def __commentLine(self):
+        """
+        Private slot to comment the current line.
         """
         if self.lexer_ is None or not self.lexer_.canBlockComment():
             return
@@ -4251,9 +4266,9 @@
         self.endUndoAction()
 
     @pyqtSlot()
-    def uncommentLine(self):
-        """
-        Public slot to uncomment the current line.
+    def __uncommentLine(self):
+        """
+        Private slot to uncomment the current line.
         """
         if self.lexer_ is None or not self.lexer_.canBlockComment():
             return
@@ -4278,9 +4293,9 @@
         self.endUndoAction()
 
     @pyqtSlot()
-    def commentSelection(self):
-        """
-        Public slot to comment the current selection.
+    def __commentSelection(self):
+        """
+        Private slot to comment the current selection.
         """
         if self.lexer_ is None or not self.lexer_.canBlockComment():
             return
@@ -4309,9 +4324,9 @@
         self.endUndoAction()
 
     @pyqtSlot()
-    def uncommentSelection(self):
-        """
-        Public slot to uncomment the current selection.
+    def __uncommentSelection(self):
+        """
+        Private slot to uncomment the current selection.
         """
         if self.lexer_ is None or not self.lexer_.canBlockComment():
             return
@@ -4361,42 +4376,140 @@
     def commentLineOrSelection(self):
         """
         Public slot to comment the current line or current selection.
-        """
-        if self.hasSelectedText():
-            self.commentSelection()
-        else:
-            self.commentLine()
+
+        If the lexer supports a block comment, that is used for commenting.
+        Otherwise a stream comment is used if that is supported. If none of
+        these are supported, the request is ignored silently.
+        """
+        if self.lexer_ is not None:
+            if self.lexer_.canBlockComment():
+                if self.hasSelectedText():
+                    self.__commentSelection()
+                else:
+                    self.__commentLine()
+            elif self.lexer_.canStreamComment():
+                # delegate to the stream comment method
+                self.streamCommentLineOrSelection()
 
     @pyqtSlot()
     def uncommentLineOrSelection(self):
         """
         Public slot to uncomment the current line or current selection.
-        """
-        if self.hasSelectedText():
-            self.uncommentSelection()
-        else:
-            self.uncommentLine()
-
-    @pyqtSlot()
-    def streamCommentLine(self):
-        """
-        Public slot to stream comment the current line.
+
+        If the lexer supports a block comment, that is used for uncommenting.
+        Otherwise a stream comment is used if that is supported. If none of
+        these are supported, the request is ignored silently.
+        """
+        if self.lexer_ is not None:
+            if self.lexer_.canBlockComment():
+                if self.hasSelectedText():
+                    self.__uncommentSelection()
+                else:
+                    self.__uncommentLine()
+            elif self.lexer_.canStreamComment():
+                # delegate to the stream uncomment method
+                self.streamUncommentLineOrSelection()
+
+    def __isStreamCommentedLine(self, line, streamCommentStr):
+        """
+        Private method to check, if the line is commented by a stream comment.
+
+        @param line text of the line to check
+        @type str
+        @param streamCommentStr dictionary containing the stream comment start and
+            end strings
+        @type dict
+        @return flag indicating a stream commented line
+        @rtype bool
+        """
+        line = line.strip()
+        return line.startswith(streamCommentStr["start"]) and line.endswith(
+            streamCommentStr["end"]
+        )
+
+    @pyqtSlot()
+    def __toggleStreamComment(self):
+        """
+        Private slot to toggle the comment of a block.
+
+        If the editor contains selected text and the start line is not commented, it
+        will be commented. Otherwise the selection will be un-commented. In case there
+        is no selected text and the current line is not commented, it will be commented.
+        If is commented, the comment block will be removed.
         """
         if self.lexer_ is None or not self.lexer_.canStreamComment():
             return
 
-        commentStr = self.lexer_.streamCommentStr()
+        streamCommentStr = self.lexer_.streamCommentStr()
+        line, index = self.getCursorPosition()
+
+        if self.hasSelectedText():
+            # Check if the selection starts with a stream comment string.
+            if self.text(self.getSelection()[0]).startswith(streamCommentStr["start"]):
+                self.streamUncommentLineOrSelection()
+            else:
+                self.streamCommentLineOrSelection()
+        elif self.__isStreamCommentedLine(self.text(line), streamCommentStr):
+            # It is a stream commented line.
+            self.streamUncommentLineOrSelection()
+        elif self.text(line).lstrip(" \t").startswith(streamCommentStr["start"]):
+            # The cursor is at the first line of a stream comment.
+            pos = len(self.text(line).replace(self.text(line).lstrip(" \t"), ""))
+            endline = line
+            lines = self.lines()
+            while endline < lines and not self.text(endline).rstrip().endswith(
+                streamCommentStr["end"]
+            ):
+                endline += 1
+
+            # Uncomment the determined block and reset the cursor position
+            self.setSelection(line, pos, endline, self.lineLength(endline))
+            self.uncommentLineOrSelection()
+            self.setCursorPosition(line, index - len(streamCommentStr["start"]))
+        elif self.text(line).rstrip().endswith(streamCommentStr["end"]):
+            # The cursor is at the last line of a stream comment.
+            begline = line
+            while begline > 0 and not self.text(begline).lstrip(" \t").startswith(
+                streamCommentStr["start"]
+            ):
+                begline -= 1
+            pos = len(self.text(begline).replace(self.text(begline).lstrip(" \t"), ""))
+
+            # Uncomment the determined block and reset the cursor position
+            self.setSelection(begline, pos, line, self.lineLength(line))
+            self.uncommentLineOrSelection()
+            self.setCursorPosition(
+                line, min(index, self.lineLength(line) - len(self.getLineSeparator()))
+            )
+        else:
+            # No selected text and the current line does not start with a stream comment
+            # string, so comment the line.
+            self.streamCommentLineOrSelection()
+
+    @pyqtSlot()
+    def __streamCommentLine(self):
+        """
+        Private slot to stream comment the current line.
+        """
+        if self.lexer_ is None or not self.lexer_.canStreamComment():
+            return
+
+        streamCommentStr = self.lexer_.streamCommentStr()
         line, index = self.getCursorPosition()
 
         self.beginUndoAction()
-        self.insertAt(commentStr["end"], line, self.lineLength(line))
-        self.insertAt(commentStr["start"], line, 0)
+        self.insertAt(
+            streamCommentStr["end"],
+            line,
+            self.lineLength(line) - len(self.getLineSeparator()),
+        )
+        self.insertAt(streamCommentStr["start"], line, 0)
         self.endUndoAction()
 
     @pyqtSlot()
-    def streamCommentSelection(self):
-        """
-        Public slot to comment the current selection.
+    def __streamCommentSelection(self):
+        """
+        Private slot to comment the current selection.
         """
         if self.lexer_ is None or not self.lexer_.canStreamComment():
             return
@@ -4404,63 +4517,158 @@
         if not self.hasSelectedText():
             return
 
-        commentStr = self.lexer_.streamCommentStr()
+        streamCommentStr = self.lexer_.streamCommentStr()
 
         # get the selection boundaries
         lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
         if indexTo == 0:
             endLine = lineTo - 1
-            endIndex = self.lineLength(endLine)
+            endIndex = self.lineLength(endLine) - len(self.getLineSeparator())
         else:
             endLine = lineTo
             endIndex = indexTo
 
         self.beginUndoAction()
-        self.insertAt(commentStr["end"], endLine, endIndex)
-        self.insertAt(commentStr["start"], lineFrom, indexFrom)
+        self.insertAt(streamCommentStr["end"], endLine, endIndex)
+        self.insertAt(streamCommentStr["start"], lineFrom, indexFrom)
 
         # change the selection accordingly
         if indexTo > 0:
-            indexTo += len(commentStr["end"])
+            indexTo += len(streamCommentStr["end"])
             if lineFrom == endLine:
-                indexTo += len(commentStr["start"])
+                indexTo += len(streamCommentStr["start"])
         self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
         self.endUndoAction()
 
     @pyqtSlot()
+    def __streamUncommentLine(self):
+        """
+        Private slot to stream uncomment the current line.
+        """
+        if self.lexer_ is None or not self.lexer_.canStreamComment():
+            return
+
+        streamCommentStr = self.lexer_.streamCommentStr()
+        line, index = self.getCursorPosition()
+
+        # check if line starts and ends with the stream comment strings
+        if not self.__isStreamCommentedLine(self.text(line), streamCommentStr):
+            return
+
+        self.beginUndoAction()
+        # 1. remove comment end string
+        self.setSelection(
+            line,
+            self.lineLength(line)
+            - len(self.getLineSeparator())
+            - len(streamCommentStr["end"]),
+            line,
+            self.lineLength(line) - len(self.getLineSeparator()),
+        )
+        self.removeSelectedText()
+
+        # 2. remove comment start string
+        lineText = self.text(line)
+        pos = len(lineText.replace(lineText.lstrip(" \t"), ""))
+        self.setSelection(line, pos, line, pos + len(streamCommentStr["start"]))
+        self.removeSelectedText()
+        self.endUndoAction()
+
+    @pyqtSlot()
+    def __streamUncommentSelection(self):
+        """
+        Private slot to stream uncomment the current selection.
+        """
+        if self.lexer_ is None or not self.lexer_.canStreamComment():
+            return
+
+        if not self.hasSelectedText():
+            return
+
+        streamCommentStr = self.lexer_.streamCommentStr()
+
+        # get the selection boundaries
+        lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
+        if indexTo == 0:
+            endLine = lineTo - 1
+            endIndex = self.lineLength(endLine) - len(self.getLineSeparator())
+        else:
+            endLine = lineTo
+            endIndex = indexTo
+
+        self.beginUndoAction()
+        self.setSelection(lineFrom, indexFrom, endLine, endIndex)
+        selTxt = self.selectedText()
+        if selTxt.endswith(streamCommentStr["end"]):
+            self.setSelection(
+                endLine, endIndex - len(streamCommentStr["end"]), endLine, endIndex
+            )
+            self.removeSelectedText()
+
+            # modify selection end accordingly
+            if indexTo > 0:
+                indexTo -= len(streamCommentStr["end"])
+        if selTxt.startswith(streamCommentStr["start"]):
+            self.setSelection(
+                lineFrom,
+                indexFrom,
+                lineFrom,
+                indexFrom + len(streamCommentStr["start"]),
+            )
+            self.removeSelectedText()
+
+            # modify selection end accordingly
+            if lineFrom == lineTo and indexTo > 0:
+                indexTo -= len(streamCommentStr["start"])
+        self.endUndoAction()
+
+        # now set the new selection
+        self.setSelection(lineFrom, indexFrom, lineTo, indexTo)
+
+    @pyqtSlot()
     def streamCommentLineOrSelection(self):
         """
         Public slot to stream comment the current line or current selection.
         """
         if self.hasSelectedText():
-            self.streamCommentSelection()
-        else:
-            self.streamCommentLine()
-
-    @pyqtSlot()
-    def boxCommentLine(self):
-        """
-        Public slot to box comment the current line.
+            self.__streamCommentSelection()
+        else:
+            self.__streamCommentLine()
+
+    @pyqtSlot()
+    def streamUncommentLineOrSelection(self):
+        """
+        Public slot to stream uncomment the current line or current selection.
+        """
+        if self.hasSelectedText():
+            self.__streamUncommentSelection()
+        else:
+            self.__streamUncommentLine()
+
+    @pyqtSlot()
+    def __boxCommentLine(self):
+        """
+        Private slot to box comment the current line.
         """
         if self.lexer_ is None or not self.lexer_.canBoxComment():
             return
 
-        commentStr = self.lexer_.boxCommentStr()
+        boxCommentStr = self.lexer_.boxCommentStr()
         line, index = self.getCursorPosition()
 
         eol = self.getLineSeparator()
         self.beginUndoAction()
         self.insertAt(eol, line, self.lineLength(line))
-        self.insertAt(commentStr["end"], line + 1, 0)
-        self.insertAt(commentStr["middle"], line, 0)
+        self.insertAt(boxCommentStr["end"], line + 1, 0)
+        self.insertAt(boxCommentStr["middle"], line, 0)
         self.insertAt(eol, line, 0)
-        self.insertAt(commentStr["start"], line, 0)
+        self.insertAt(boxCommentStr["start"], line, 0)
         self.endUndoAction()
 
     @pyqtSlot()
-    def boxCommentSelection(self):
-        """
-        Public slot to box comment the current selection.
+    def __boxCommentSelection(self):
+        """
+        Private slot to box comment the current selection.
         """
         if self.lexer_ is None or not self.lexer_.canBoxComment():
             return
@@ -4468,7 +4676,7 @@
         if not self.hasSelectedText():
             return
 
-        commentStr = self.lexer_.boxCommentStr()
+        boxCommentStr = self.lexer_.boxCommentStr()
 
         # get the selection boundaries
         lineFrom, indexFrom, lineTo, indexTo = self.getSelection()
@@ -4477,14 +4685,14 @@
         self.beginUndoAction()
         # iterate over the lines
         for line in range(lineFrom, endLine + 1):
-            self.insertAt(commentStr["middle"], line, 0)
+            self.insertAt(boxCommentStr["middle"], line, 0)
 
         # now do the comments before and after the selection
         eol = self.getLineSeparator()
         self.insertAt(eol, endLine, self.lineLength(endLine))
-        self.insertAt(commentStr["end"], endLine + 1, 0)
+        self.insertAt(boxCommentStr["end"], endLine + 1, 0)
         self.insertAt(eol, lineFrom, 0)
-        self.insertAt(commentStr["start"], lineFrom, 0)
+        self.insertAt(boxCommentStr["start"], lineFrom, 0)
 
         # change the selection accordingly
         self.setSelection(lineFrom, 0, endLine + 3, 0)
@@ -4496,9 +4704,9 @@
         Public slot to box comment the current line or current selection.
         """
         if self.hasSelectedText():
-            self.boxCommentSelection()
-        else:
-            self.boxCommentLine()
+            self.__boxCommentSelection()
+        else:
+            self.__boxCommentLine()
 
     ###########################################################################
     ## Indentation handling methods below
@@ -7574,6 +7782,7 @@
         with contextlib.suppress(AttributeError):
             self.setCaretWidth(self.caretWidth)
         self.__updateReadOnly(False)
+        # TODO: realize this with a QFileSystemWatcher in ViewManager
         if (
             self.vm.editorsCheckFocusInEnabled()
             and not self.inReopenPrompt

eric ide

mercurial