--- 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