--- a/src/eric7/QScintilla/Editor.py Tue Oct 31 09:23:05 2023 +0100 +++ b/src/eric7/QScintilla/Editor.py Wed Nov 29 14:23:36 2023 +0100 @@ -31,7 +31,7 @@ pyqtSignal, pyqtSlot, ) -from PyQt6.QtGui import QActionGroup, QFont, QPainter, QPalette, QPixmap +from PyQt6.QtGui import QAction, QActionGroup, QFont, QPainter, QPalette, QPixmap from PyQt6.QtPrintSupport import ( QAbstractPrintDialog, QPrintDialog, @@ -149,6 +149,8 @@ WarningCode = 1 WarningPython = 2 WarningStyle = 3 + WarningInfo = 4 + WarningError = 5 # Autocompletion icon definitions ClassID = 1 @@ -251,7 +253,7 @@ self.syntaxerrors = {} # key: marker handle # value: list of (error message, error index) - self.warnings = {} + self._warnings = {} # key: marker handle # value: list of (warning message, warning type) self.notcoveredMarkers = [] # just a list of marker handles @@ -315,6 +317,16 @@ self.__markedText = "" self.__searchIndicatorLines = [] + # set the autosave attributes + self.__autosaveInterval = Preferences.getEditor("AutosaveIntervalSeconds") + self.__autosaveManuallyDisabled = False + + # initialize the autosave timer + self.__autosaveTimer = QTimer(self) + self.__autosaveTimer.setObjectName("AutosaveTimer") + self.__autosaveTimer.setSingleShot(True) + self.__autosaveTimer.timeout.connect(self.__autosave) + # initialize some spellchecking stuff self.spell = None self.lastLine = 0 @@ -548,10 +560,6 @@ if bnd.width() < req.width() or bnd.height() < req.height(): self.resize(bnd) - # set the autosave flag - self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0 - self.autosaveManuallyDisabled = False - # code coverage related attributes self.__coverageFile = "" @@ -571,7 +579,7 @@ self.__encodingChanged(editor.encoding, propagate=False) self.__spellLanguageChanged(editor.getSpellingLanguage(), propagate=False) # link the warnings to the original editor - self.warnings = editor.warnings + self._warnings = editor._warnings self.setAcceptDrops(True) @@ -957,7 +965,7 @@ self.tr("Autosave enabled"), self.__autosaveEnable ) self.menuActs["AutosaveEnable"].setCheckable(True) - self.menuActs["AutosaveEnable"].setChecked(self.autosaveEnabled) + self.menuActs["AutosaveEnable"].setChecked(self.__autosaveInterval > 0) self.menuActs["TypingAidsEnabled"] = self.menu.addAction( self.tr("Typing aids enabled"), self.__toggleTypingAids ) @@ -1539,6 +1547,7 @@ self.tr("""No export format given. Aborting..."""), ) + @pyqtSlot() def __showContextMenuLanguages(self): """ Private slot handling the aboutToShow signal of the languages context @@ -1605,7 +1614,7 @@ def __languageChanged(self, language, propagate=True): """ - Private slot handling a change of a connected editor's language. + Private method handling a change of a connected editor's language. @param language language to be set (string) @param propagate flag indicating to propagate the change (boolean) @@ -1710,12 +1719,14 @@ else: self.supportedLanguages[self.apiLanguage][2].setChecked(True) + @pyqtSlot() def projectLexerAssociationsChanged(self): """ Public slot to handle changes of the project lexer associations. """ self.setLanguage(self.fileName) + @pyqtSlot() def __showContextMenuEncodings(self): """ Private slot handling the aboutToShow signal of the encodings context @@ -1740,12 +1751,15 @@ with contextlib.suppress(AttributeError, KeyError): (self.supportedEncodings[self.__normalizedEncoding()].setChecked(True)) + @pyqtSlot(str) def __encodingChanged(self, encoding, propagate=True): """ Private slot to handle a change of the encoding. - @param encoding changed encoding (string) - @param propagate flag indicating to propagate the change (boolean) + @param encoding changed encoding + @type str + @param propagate flag indicating to propagate the change + @type bool """ self.encoding = encoding self.__checkEncoding() @@ -1770,6 +1784,7 @@ .replace("-selected", "") ) + @pyqtSlot() def __showContextMenuEol(self): """ Private slot handling the aboutToShow signal of the eol context menu. @@ -1793,6 +1808,7 @@ with contextlib.suppress(AttributeError, TypeError): self.supportedEols[self.getLineSeparator()].setChecked(True) + @pyqtSlot() def __eolChanged(self): """ Private slot to handle a change of the eol mode. @@ -1805,6 +1821,7 @@ self.eolChanged.emit(eol) self.inEolChanged = False + @pyqtSlot() def __showContextMenuSpellCheck(self): """ Private slot handling the aboutToShow signal of the spell check @@ -1822,6 +1839,7 @@ self.showMenu.emit("SpellCheck", self.spellCheckMenu, self) + @pyqtSlot() def __showContextMenuSpellLanguages(self): """ Private slot handling the aboutToShow signal of the spell check @@ -1840,6 +1858,7 @@ self.__setSpellingLanguage(language) self.spellLanguageChanged.emit(language) + @pyqtSlot() def __checkSpellLanguage(self): """ Private slot to check the selected spell check language action. @@ -1848,6 +1867,7 @@ with contextlib.suppress(AttributeError, KeyError): self.supportedSpellLanguages[language].setChecked(True) + @pyqtSlot(str) def __spellLanguageChanged(self, language, propagate=True): """ Private slot to handle a change of the spell check language. @@ -1867,11 +1887,13 @@ def __bindLexer(self, filename, pyname=""): """ - Private slot to set the correct lexer depending on language. + Private method to set the correct lexer depending on language. @param filename filename used to determine the associated lexer - language (string) - @param pyname name of the pygments lexer to use (string) + language + @type str + @param pyname name of the pygments lexer to use + @type str """ if self.lexer_ is not None and ( self.lexer_.lexer() == "container" or self.lexer_.lexer() is None @@ -1983,11 +2005,13 @@ self.lexer_.setDefaultColor(self.lexer_.color(0)) self.lexer_.setDefaultPaper(self.lexer_.paper(0)) + @pyqtSlot(int) def __styleNeeded(self, position): """ Private slot to handle the need for more styling. - @param position end position, that needs styling (integer) + @param position end position, that needs styling + @type int """ self.lexer_.styleText(self.getEndStyled(), position) @@ -2033,10 +2057,11 @@ def __bindCompleter(self, filename): """ - Private slot to set the correct typing completer depending on language. + Private method to set the correct typing completer depending on language. @param filename filename used to determine the associated typing - completer language (string) + completer language + @type str """ if self.completer is not None: self.completer.setEnabled(False) @@ -2061,6 +2086,7 @@ """ return self.completer + @pyqtSlot(bool) def __modificationChanged(self, m): """ Private slot to handle the modificationChanged signal. @@ -2069,6 +2095,7 @@ m and self. @param m modification status + @type bool """ if not m and bool(self.fileName) and pathlib.Path(self.fileName).exists(): self.lastModified = pathlib.Path(self.fileName).stat().st_mtime @@ -2076,6 +2103,10 @@ self.undoAvailable.emit(self.isUndoAvailable()) self.redoAvailable.emit(self.isRedoAvailable()) + if not m: + self.__autosaveTimer.stop() + + @pyqtSlot(int, int) def __cursorPositionChanged(self, line, index): """ Private slot to handle the cursorPositionChanged signal. @@ -2084,12 +2115,13 @@ line and pos. @param line line number of the cursor + @type int @param index position in line of the cursor + @type int """ self.cursorChanged.emit(self.fileName, line + 1, index) if Preferences.getEditor("MarkOccurrencesEnabled"): - self.__markOccurrencesTimer.stop() self.__markOccurrencesTimer.start() if self.lastLine != line: @@ -2112,6 +2144,7 @@ self.lastLine = line self.lastIndex = index + @pyqtSlot() def __modificationReadOnly(self): """ Private slot to handle the modificationAttempted signal. @@ -2382,40 +2415,52 @@ @param annotationLinesAdded number of added/deleted annotation lines (integer) """ - if ( - mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT) - and linesAdded != 0 - and self.breaks - ): - bps = [] # list of breakpoints - for handle, (ln, cond, temp, enabled, ignorecount) in self.breaks.items(): - line = self.markerLine(handle) + 1 - if ln != line: - bps.append((ln, line)) - self.breaks[handle] = (line, cond, temp, enabled, ignorecount) - self.inLinesChanged = True - for ln, line in sorted(bps, reverse=linesAdded > 0): - index1 = self.breakpointModel.getBreakPointIndex(self.fileName, ln) - index2 = self.breakpointModel.index(index1.row(), 1) - self.breakpointModel.setData(index2, line) - self.inLinesChanged = False + if mtype & (self.SC_MOD_INSERTTEXT | self.SC_MOD_DELETETEXT): + # 1. set/reset the autosave timer + if self.__autosaveInterval > 0: + self.__autosaveTimer.start(self.__autosaveInterval * 1000) + + # 2. move breakpoints if a line was inserted or deleted + if linesAdded != 0 and self.breaks: + bps = [] # list of breakpoints + for handle, ( + ln, + cond, + temp, + enabled, + ignorecount, + ) in self.breaks.items(): + line = self.markerLine(handle) + 1 + if ln != line: + bps.append((ln, line)) + self.breaks[handle] = (line, cond, temp, enabled, ignorecount) + self.inLinesChanged = True + for ln, line in sorted(bps, reverse=linesAdded > 0): + index1 = self.breakpointModel.getBreakPointIndex(self.fileName, ln) + index2 = self.breakpointModel.index(index1.row(), 1) + self.breakpointModel.setData(index2, line) + self.inLinesChanged = False def __restoreBreakpoints(self): """ Private method to restore the breakpoints. """ - for handle in list(self.breaks.keys()): + for handle in self.breaks: self.markerDeleteHandle(handle) self.__addBreakPoints(QModelIndex(), 0, self.breakpointModel.rowCount() - 1) self.__markerMap.update() + @pyqtSlot(QModelIndex, int, int) def __deleteBreakPoints(self, parentIndex, start, end): """ Private slot to delete breakpoints. - @param parentIndex index of parent item (QModelIndex) - @param start start row (integer) - @param end end row (integer) + @param parentIndex index of parent item + @type QModelIndex + @param start start row + @type int + @param end end row + @type int """ for row in range(start, end + 1): index = self.breakpointModel.index(row, 0, parentIndex) @@ -2423,35 +2468,43 @@ if fn == self.fileName: self.clearBreakpoint(lineno) + @pyqtSlot(QModelIndex, QModelIndex) def __changeBreakPoints(self, startIndex, endIndex): """ Private slot to set changed breakpoints. @param startIndex start index of the breakpoints being changed - (QModelIndex) + @type QModelIndex @param endIndex end index of the breakpoints being changed - (QModelIndex) + @type QModelIndex """ if not self.inLinesChanged: self.__addBreakPoints(QModelIndex(), startIndex.row(), endIndex.row()) + @pyqtSlot(QModelIndex, QModelIndex) def __breakPointDataAboutToBeChanged(self, startIndex, endIndex): """ Private slot to handle the dataAboutToBeChanged signal of the breakpoint model. - @param startIndex start index of the rows to be changed (QModelIndex) - @param endIndex end index of the rows to be changed (QModelIndex) + @param startIndex start index of the rows to be changed + @type QModelIndex + @param endIndex end index of the rows to be changed + @type QModelIndex """ self.__deleteBreakPoints(QModelIndex(), startIndex.row(), endIndex.row()) + @pyqtSlot(QModelIndex, int, int) def __addBreakPoints(self, parentIndex, start, end): """ Private slot to add breakpoints. - @param parentIndex index of parent item (QModelIndex) - @param start start row (integer) - @param end end row (integer) + @param parentIndex index of parent item + @type QModelIndex + @param start start row + @type int + @param end end row + @type int """ for row in range(start, end + 1): index = self.breakpointModel.index(row, 0, parentIndex) @@ -2482,14 +2535,10 @@ for handle in self.breaks: if self.markerLine(handle) == line - 1: - break - else: - # not found, simply ignore it - return - - del self.breaks[handle] - self.markerDeleteHandle(handle) - self.__markerMap.update() + del self.breaks[handle] + self.markerDeleteHandle(handle) + self.__markerMap.update() + return def newBreakpointWithProperties(self, line, properties): """ @@ -2615,6 +2664,7 @@ """ return len(self.breaks) > 0 + @pyqtSlot() def __menuToggleTemporaryBreakpoint(self): """ Private slot to handle the 'Toggle temporary breakpoint' context menu @@ -2626,6 +2676,7 @@ self.__toggleBreakpoint(self.line, 1) self.line = -1 + @pyqtSlot() def menuToggleBreakpoint(self): """ Public slot to handle the 'Toggle breakpoint' context menu action. @@ -2636,6 +2687,7 @@ self.__toggleBreakpoint(self.line) self.line = -1 + @pyqtSlot() def __menuToggleBreakpointEnabled(self): """ Private slot to handle the 'Enable/Disable breakpoint' context menu @@ -2647,11 +2699,13 @@ self.__toggleBreakpointEnabled(self.line) self.line = -1 + @pyqtSlot() def menuEditBreakpoint(self, line=None): """ Public slot to handle the 'Edit breakpoint' context menu action. - @param line linenumber of the breakpoint to edit + @param line line number of the breakpoint to edit + @type int """ from eric7.Debugger.EditBreakpointDialog import EditBreakpointDialog @@ -2702,6 +2756,7 @@ self.line = -1 + @pyqtSlot() def menuNextBreakpoint(self): """ Public slot to handle the 'Next breakpoint' context menu action. @@ -2719,6 +2774,7 @@ self.setCursorPosition(bpline, 0) self.ensureLineVisible(bpline) + @pyqtSlot() def menuPreviousBreakpoint(self): """ Public slot to handle the 'Previous breakpoint' context menu action. @@ -2736,6 +2792,7 @@ self.setCursorPosition(bpline, 0) self.ensureLineVisible(bpline) + @pyqtSlot() def __menuClearBreakpoints(self): """ Private slot to handle the 'Clear all breakpoints' context menu action. @@ -2744,9 +2801,10 @@ def __clearBreakpoints(self, fileName): """ - Private slot to clear all breakpoints. - - @param fileName name of the file (string) + Private method to clear all breakpoints. + + @param fileName name of the file + @type str """ idxList = [] for ln, _, _, _, _ in self.breaks.values(): @@ -2816,6 +2874,7 @@ """ return len(self.bookmarks) > 0 + @pyqtSlot() def menuToggleBookmark(self): """ Public slot to handle the 'Toggle bookmark' context menu action. @@ -2826,6 +2885,7 @@ self.toggleBookmark(self.line) self.line = -1 + @pyqtSlot() def nextBookmark(self): """ Public slot to handle the 'Next bookmark' context menu action. @@ -2843,6 +2903,7 @@ self.setCursorPosition(bmline, 0) self.ensureLineVisible(bmline) + @pyqtSlot() def previousBookmark(self): """ Public slot to handle the 'Previous bookmark' context menu action. @@ -2860,6 +2921,7 @@ self.setCursorPosition(bmline, 0) self.ensureLineVisible(bmline) + @pyqtSlot() def clearBookmarks(self): """ Public slot to handle the 'Clear all bookmarks' context menu action. @@ -2874,6 +2936,7 @@ ## Printing methods below ########################################################################### + @pyqtSlot() def printFile(self): """ Public slot to print the text. @@ -2913,6 +2976,7 @@ sb.showMessage(self.tr("Printing aborted"), 2000) QApplication.processEvents() + @pyqtSlot() def printPreviewFile(self): """ Public slot to show a print preview of the text. @@ -2966,6 +3030,7 @@ """ return self.__hasTaskMarkers + @pyqtSlot() def nextTask(self): """ Public slot to handle the 'Next task' context menu action. @@ -2983,6 +3048,7 @@ self.setCursorPosition(taskline, 0) self.ensureLineVisible(taskline) + @pyqtSlot() def previousTask(self): """ Public slot to handle the 'Previous task' context menu action. @@ -3000,6 +3066,7 @@ self.setCursorPosition(taskline, 0) self.ensureLineVisible(taskline) + @pyqtSlot() def extractTasks(self): """ Public slot to extract all tasks. @@ -3061,6 +3128,7 @@ painter.end() return pixmap + @pyqtSlot() def __initOnlineChangeTrace(self): """ Private slot to initialize the online change trace. @@ -3078,6 +3146,7 @@ ) self.textChanged.connect(self.__resetOnlineChangeTraceTimer) + @pyqtSlot() def __reinitOnlineChangeTrace(self): """ Private slot to re-initialize the online change trace. @@ -3094,6 +3163,7 @@ self.__onlineChangeTraceTimer.stop() self.__onlineChangeTraceTimer.start() + @pyqtSlot() def __onlineChangeTraceTimerTimeout(self): """ Private slot to mark added and changed lines. @@ -3126,6 +3196,7 @@ self.changeMarkersUpdated.emit(self) self.__markerMap.update() + @pyqtSlot() def resetOnlineChangeTraceInfo(self): """ Public slot to reset the online change trace info. @@ -3148,6 +3219,7 @@ self.changeMarkersUpdated.emit(self) self.__markerMap.update() + @pyqtSlot() def __deleteAllChangeMarkers(self): """ Private slot to delete all change markers. @@ -3188,6 +3260,7 @@ """ return self.__hasChangeMarkers + @pyqtSlot() def nextChange(self): """ Public slot to handle the 'Next change' context menu action. @@ -3205,6 +3278,7 @@ self.setCursorPosition(changeline, 0) self.ensureLineVisible(changeline) + @pyqtSlot() def previousChange(self): """ Public slot to handle the 'Previous change' context menu action. @@ -3304,7 +3378,7 @@ def readFile(self, fn, createIt=False, encoding=""): """ - Public slot to read the text from a file. + Public method to read the text from a file. @param fn filename to read from (string) @param createIt flag indicating the creation of a new file, if the @@ -3360,6 +3434,7 @@ self.setModified(modified) self.lastModified = pathlib.Path(fn).stat().st_mtime + @pyqtSlot() def __convertTabs(self): """ Private slot to convert tabulators to spaces. @@ -3393,11 +3468,14 @@ def writeFile(self, fn, backup=True): """ - Public slot to write the text to a file. - - @param fn filename to write to (string) - @param backup flag indicating to save a backup (boolean) - @return flag indicating success (boolean) + Public method to write the text to a file. + + @param fn filename to write to + @type str + @param backup flag indicating to save a backup + @type bool + @return flag indicating success + @rtype bool """ config = self.__loadEditorConfigObject(fn) @@ -3578,6 +3656,8 @@ self.__loadEditorConfig(fn) self.editorAboutToBeSaved.emit(self.fileName) + if self.__autosaveTimer.isActive(): + self.__autosaveTimer.stop() if self.writeFile(fn): if saveas: self.__clearBreakpoints(self.fileName) @@ -3619,11 +3699,12 @@ def saveFileAs(self, path=None): """ - Public slot to save a file with a new name. - - @param path directory to save the file in (string) - @return tuple of two values (boolean, string) giving a success - indicator and the name of the saved file + Public method to save a file with a new name. + + @param path directory to save the file in + @type str + @return tuple containing a success indicator and the name of the saved file + @rtype tuple of (bool, str) """ return self.saveFile(True, path) @@ -3669,7 +3750,7 @@ def handleRenamed(self, fn): """ - Public slot to handle the editorRenamed signal. + Public method to handle the editorRenamed signal. @param fn filename to be set for the editor (string). """ @@ -3687,11 +3768,13 @@ self.vm.setEditorName(self, self.fileName) self.__updateReadOnly(True) + @pyqtSlot(str) def fileRenamed(self, fn): """ Public slot to handle the editorRenamed signal. - @param fn filename to be set for the editor (string). + @param fn filename to be set for the editor + @type str. """ self.handleRenamed(fn) if not self.inFileRenamed: @@ -3705,7 +3788,7 @@ def ensureVisible(self, line, expand=False): """ - Public slot to ensure, that the specified line is visible. + Public method to ensure, that the specified line is visible. @param line line number to make visible @type int @@ -3722,7 +3805,7 @@ def ensureVisibleTop(self, line, expand=False): """ - Public slot to ensure, that the specified line is visible at the top + Public method to ensure, that the specified line is visible at the top of the editor. @param line line number to make visible @@ -3744,9 +3827,12 @@ """ Private slot to handle the marginClicked signal. - @param margin id of the clicked margin (integer) - @param line line number of the click (integer) - @param modifiers keyboard modifiers (Qt.KeyboardModifiers) + @param margin id of the clicked margin + @type int + @param line line number of the click + @type int + @param modifiers keyboard modifiers + @type Qt.KeyboardModifiers """ if margin == self.__bmMargin: self.toggleBookmark(line + 1) @@ -3758,6 +3844,7 @@ elif self.markersAtLine(line) & (1 << self.warning): self.__showWarning(line) + @pyqtSlot() def handleMonospacedEnable(self): """ Public slot to handle the Use Monospaced Font context menu entry. @@ -3774,16 +3861,22 @@ self.setMonospaced(False) self.__setMarginsDisplay() - def getWordBoundaries(self, line, index, useWordChars=True): + def getWordBoundaries(self, line, index, useWordChars=True, forCompletion=False): """ Public method to get the word boundaries at a position. - @param line number of line to look at (int) - @param index position to look at (int) + @param line number of line to look at + @type int + @param index position to look at + @type int @param useWordChars flag indicating to use the wordCharacters - method (boolean) + method (defaults to True) + @type bool (optional) + @param forCompletion flag indicating a modification for completions (defaults + to False) + @type bool (optional) @return tuple with start and end indexes of the word at the position - (integer, integer) + @rtype tuple of (int, int) """ wc = self.wordCharacters() if wc is None or not useWordChars: @@ -3791,6 +3884,8 @@ else: wc = re.sub(r"\w", "", wc) pattern = r"\b[\w{0}]+\b".format(re.escape(wc)) + if forCompletion: + pattern += "=?" rx = ( re.compile(pattern) if self.caseSensitive() @@ -3805,19 +3900,29 @@ return (index, index) - def getWord(self, line, index, direction=0, useWordChars=True): + def getWord(self, line, index, direction=0, useWordChars=True, forCompletion=False): """ Public method to get the word at a position. - @param line number of line to look at (int) - @param index position to look at (int) + @param line number of line to look at + @type int + @param index position to look at + @type int @param direction direction to look in (0 = whole word, 1 = left, 2 = right) + @type int @param useWordChars flag indicating to use the wordCharacters - method (boolean) - @return the word at that position (string) - """ - start, end = self.getWordBoundaries(line, index, useWordChars) + method (defaults to True) + @type bool (optional) + @param forCompletion flag indicating a modification for completions (defaults + to False) + @type bool (optional) + @return the word at that position + @rtype str + """ + start, end = self.getWordBoundaries( + line, index, useWordChars=useWordChars, forCompletion=forCompletion + ) if direction == 1: end = index elif direction == 2: @@ -4080,6 +4185,7 @@ else: return line.strip().startswith(commentStr) + @pyqtSlot() def toggleCommentBlock(self): """ Public slot to toggle the comment of a block. @@ -4127,6 +4233,7 @@ self.uncommentLineOrSelection() self.setCursorPosition(line, index - len(commentStr)) + @pyqtSlot() def commentLine(self): """ Public slot to comment the current line. @@ -4144,6 +4251,7 @@ self.insertAt(self.lexer_.commentStr(), line, pos) self.endUndoAction() + @pyqtSlot() def uncommentLine(self): """ Public slot to uncomment the current line. @@ -4170,6 +4278,7 @@ self.removeSelectedText() self.endUndoAction() + @pyqtSlot() def commentSelection(self): """ Public slot to comment the current selection. @@ -4200,6 +4309,7 @@ self.setSelection(lineFrom, 0, endLine + 1, 0) self.endUndoAction() + @pyqtSlot() def uncommentSelection(self): """ Public slot to uncomment the current selection. @@ -4248,6 +4358,7 @@ self.setSelection(lineFrom, indexFrom, lineTo, indexTo) self.endUndoAction() + @pyqtSlot() def commentLineOrSelection(self): """ Public slot to comment the current line or current selection. @@ -4257,6 +4368,7 @@ else: self.commentLine() + @pyqtSlot() def uncommentLineOrSelection(self): """ Public slot to uncomment the current line or current selection. @@ -4266,6 +4378,7 @@ else: self.uncommentLine() + @pyqtSlot() def streamCommentLine(self): """ Public slot to stream comment the current line. @@ -4281,6 +4394,7 @@ self.insertAt(commentStr["start"], line, 0) self.endUndoAction() + @pyqtSlot() def streamCommentSelection(self): """ Public slot to comment the current selection. @@ -4314,6 +4428,7 @@ self.setSelection(lineFrom, indexFrom, lineTo, indexTo) self.endUndoAction() + @pyqtSlot() def streamCommentLineOrSelection(self): """ Public slot to stream comment the current line or current selection. @@ -4323,6 +4438,7 @@ else: self.streamCommentLine() + @pyqtSlot() def boxCommentLine(self): """ Public slot to box comment the current line. @@ -4342,6 +4458,7 @@ self.insertAt(commentStr["start"], line, 0) self.endUndoAction() + @pyqtSlot() def boxCommentSelection(self): """ Public slot to box comment the current selection. @@ -4374,6 +4491,7 @@ self.setSelection(lineFrom, 0, endLine + 3, 0) self.endUndoAction() + @pyqtSlot() def boxCommentLineOrSelection(self): """ Public slot to box comment the current line or current selection. @@ -4451,6 +4569,7 @@ indexEnd = 0 self.setSelection(lineFrom, indexStart, lineTo, indexEnd) + @pyqtSlot() def indentLineOrSelection(self): """ Public slot to indent the current line or current selection. @@ -4460,6 +4579,7 @@ else: self.__indentLine(True) + @pyqtSlot() def unindentLineOrSelection(self): """ Public slot to unindent the current line or current selection. @@ -4469,6 +4589,7 @@ else: self.__indentLine(False) + @pyqtSlot() def smartIndentLineOrSelection(self): """ Public slot to indent current line smartly. @@ -4486,7 +4607,7 @@ def gotoLine(self, line, pos=1, firstVisible=False, expand=False): """ - Public slot to jump to the beginning of a line. + Public method to jump to the beginning of a line. @param line line number to go to @type int @@ -4504,6 +4625,7 @@ else: self.ensureVisible(line, expand) + @pyqtSlot() def __textChanged(self): """ Private slot to handle a change of the editor text. @@ -4514,6 +4636,7 @@ """ QTimer.singleShot(0, self.__saveLastEditPosition) + @pyqtSlot() def __saveLastEditPosition(self): """ Private slot to record the last edit position. @@ -4591,6 +4714,7 @@ ## Setup methods below ########################################################################### + @pyqtSlot() def readSettings(self): """ Public slot to read the settings into our lexer. @@ -4646,7 +4770,12 @@ self.__setCallTips() # set the autosave flags - self.autosaveEnabled = Preferences.getEditor("AutosaveInterval") > 0 + self.__autosaveInterval = Preferences.getEditor("AutosaveIntervalSeconds") + if self.__autosaveInterval == 0: + self.__autosaveTimer.stop() + else: + if self.isModified(): + self.__autosaveTimer.start(self.__autosaveInterval * 1000) if Preferences.getEditor("MiniContextMenu") != self.miniMenu: # regenerate context menu @@ -4658,7 +4787,7 @@ ) self.menuActs["MonospacedFont"].setChecked(self.useMonospaced) self.menuActs["AutosaveEnable"].setChecked( - self.autosaveEnabled and not self.autosaveManuallyDisabled + self.__autosaveInterval > 0 and not self.__autosaveManuallyDisabled ) # regenerate the margins context menu(s) @@ -4802,6 +4931,7 @@ QsciScintilla.FoldStyle.NoFoldStyle.value, self.__foldMargin ) + @pyqtSlot() def __resizeLinenoMargin(self): """ Private slot to resize the line numbers margin. @@ -5130,6 +5260,7 @@ else: self.setAutoCompletionSource(QsciScintilla.AutoCompletionSource.AcsAll) + @pyqtSlot() def __toggleAutoCompletionEnable(self): """ Private slot to handle the Enable Autocompletion context menu entry. @@ -5143,11 +5274,13 @@ ## Support for autocompletion hook methods ################################################################# + @pyqtSlot(int) def __charAdded(self, charNumber): """ Private slot called to handle the user entering a character. - @param charNumber value of the character entered (integer) + @param charNumber value of the character entered + @type int """ char = chr(charNumber) # update code documentation viewer @@ -5199,6 +5332,7 @@ wseps = self.lexer_.autoCompletionWordSeparators() return any(wsep.endswith(ch) for wsep in wseps) + @pyqtSlot() def __autocompletionCancelled(self): """ Private slot to handle the cancellation of an auto-completion list. @@ -5447,12 +5581,15 @@ """ self.__acCache.clear() + @pyqtSlot(int, str) def __completionListSelected(self, listId, txt): """ Private slot to handle the selection from the completion list. - @param listId the ID of the user list (should be 1 or 2) (integer) - @param txt the selected text (string) + @param listId the ID of the user list (should be 1 or 2) + @type int + @param txt the selected text + @type str """ # custom completions via plug-ins if listId == EditorAutoCompletionListID: @@ -5467,7 +5604,7 @@ line, col = self.getCursorPosition() else: line, col = self.getCursorPosition() - wLeft = self.getWordLeft(line, col) + wLeft = self.getWord(line, col, 1, forCompletion=True) # word left if not txt.startswith(wLeft): self.selectCurrentWord() self.removeSelectedText() @@ -5711,6 +5848,7 @@ ## Methods needed by the code documentation viewer ################################################################# + @pyqtSlot() def __showCodeInfo(self): """ Private slot to handle the context menu action to show code info. @@ -5764,6 +5902,7 @@ elif self.__marginNumber(pos.x()) == self.__foldMargin: self.foldMarginMenu.popup(self.mapToGlobal(pos)) + @pyqtSlot() def __aboutToShowContextMenu(self): """ Private slot handling the aboutToShow signal of the context menu. @@ -5828,6 +5967,7 @@ self.showMenu.emit("Main", self.menu, self) + @pyqtSlot() def __showContextMenuAutocompletion(self): """ Private slot called before the autocompletion menu is shown. @@ -5839,6 +5979,7 @@ self.showMenu.emit("Autocompletion", self.autocompletionMenu, self) + @pyqtSlot() def __showContextMenuShow(self): """ Private slot called before the show menu is shown. @@ -5884,6 +6025,7 @@ self.showMenu.emit("Show", self.menuShow, self) + @pyqtSlot() def __showContextMenuGraphics(self): """ Private slot handling the aboutToShow signal of the diagrams context @@ -5898,6 +6040,7 @@ self.showMenu.emit("Graphics", self.graphicsMenu, self) + @pyqtSlot(QMenu) def __showContextMenuMargin(self, menu): """ Private slot handling the aboutToShow signal of the margins context @@ -5956,7 +6099,7 @@ if menu is self.indicMarginMenu: hasSyntaxErrors = bool(self.syntaxerrors) - hasWarnings = bool(self.warnings) + hasWarnings = bool(self._warnings) hasNotCoveredMarkers = bool(self.notcoveredMarkers) self.marginMenuActs["GotoSyntaxError"].setEnabled(hasSyntaxErrors) @@ -5994,6 +6137,7 @@ self.showMenu.emit("Margin", menu, self) + @pyqtSlot() def __showContextMenuChecks(self): """ Private slot handling the aboutToShow signal of the checks context @@ -6001,6 +6145,7 @@ """ self.showMenu.emit("Checks", self.checksMenu, self) + @pyqtSlot() def __showContextMenuTools(self): """ Private slot handling the aboutToShow signal of the tools context @@ -6008,6 +6153,7 @@ """ self.showMenu.emit("Tools", self.toolsMenu, self) + @pyqtSlot() def __showContextMenuFormatting(self): """ Private slot handling the aboutToShow signal of the code formatting context @@ -6027,6 +6173,7 @@ self.__convertTabs() self.__checkEncoding() + @pyqtSlot() def __contextSave(self): """ Private slot handling the save context menu entry. @@ -6035,6 +6182,7 @@ if ok: self.vm.setEditorName(self, self.fileName) + @pyqtSlot() def __contextSaveAs(self): """ Private slot handling the save as context menu entry. @@ -6043,24 +6191,28 @@ if ok: self.vm.setEditorName(self, self.fileName) + @pyqtSlot() def __contextSaveCopy(self): """ Private slot handling the save copy context menu entry. """ self.saveFileCopy() + @pyqtSlot() def __contextClose(self): """ Private slot handling the close context menu entry. """ self.vm.closeEditor(self) + @pyqtSlot() def __newView(self): """ Private slot to create a new view to an open document. """ self.vm.newEditorView(self.fileName, self, self.filetype) + @pyqtSlot() def __newViewNewSplit(self): """ Private slot to create a new view to an open document. @@ -6068,18 +6220,21 @@ self.vm.addSplit() self.vm.newEditorView(self.fileName, self, self.filetype) + @pyqtSlot() def __selectAll(self): """ Private slot handling the select all context menu action. """ self.selectAll(True) + @pyqtSlot() def __deselectAll(self): """ Private slot handling the deselect all context menu action. """ self.selectAll(False) + @pyqtSlot() def joinLines(self): """ Public slot to join the current line with the next one. @@ -6121,6 +6276,7 @@ self.insertAt(" ", curLine, startIndex) self.endUndoAction() + @pyqtSlot() def shortenEmptyLines(self): """ Public slot to compress lines consisting solely of whitespace @@ -6135,26 +6291,38 @@ ok = self.findNextTarget() self.endUndoAction() + @pyqtSlot() def __autosaveEnable(self): """ Private slot handling the autosave enable context menu action. """ if self.menuActs["AutosaveEnable"].isChecked(): - self.autosaveManuallyDisabled = False - else: - self.autosaveManuallyDisabled = True - - def shouldAutosave(self): - """ - Public slot to check the autosave flags. + self.__autosaveManuallyDisabled = False + else: + self.__autosaveManuallyDisabled = True + + def __shouldAutosave(self): + """ + Private method to check the autosave flags. @return flag indicating this editor should be saved (boolean) """ return ( bool(self.fileName) - and not self.autosaveManuallyDisabled + and not self.__autosaveManuallyDisabled and not self.isReadOnly() - ) + and self.isModified() + ) + + def __autosave(self): + """ + Private slot to save the contents of the editor automatically. + + It is only saved by the autosave timer after an initial save (i.e. it already + has a name). + """ + if self.__shouldAutosave(): + self.saveFile() def checkSyntax(self): """ @@ -6197,6 +6365,7 @@ self.text(), ) + @pyqtSlot(str, str) def __processSyntaxCheckError(self, fn, msg): """ Private slot to report an error message of a syntax check. @@ -6216,15 +6385,18 @@ self.updateVerticalScrollBar() + @pyqtSlot(str, dict) def __processSyntaxCheckResult(self, fn, problems): """ Private slot to report the resulting messages of a syntax check. - @param fn filename of the checked file (str) + @param fn filename of the checked file + @type str @param problems dictionary with the keys 'error' and 'warnings' which hold a list containing details about the error/ warnings (file name, line number, column, codestring (only at syntax - errors), the message) (dict) + errors), the message) + @type dict """ # Check if it's the requested file, otherwise ignore signal if fn != self.fileName and (bool(self.fileName) or fn != "(Unnamed)"): @@ -6238,16 +6410,15 @@ _fn, lineno, col, code, msg = error self.toggleSyntaxError(lineno, col, True, msg) - warnings = problems.get("py_warnings", []) - for _fn, lineno, col, _code, msg in warnings: + for _fn, lineno, col, _code, msg in problems.get("py_warnings", []): self.toggleWarning(lineno, col, True, msg, warningType=Editor.WarningPython) - warnings = problems.get("warnings", []) - for _fn, lineno, col, _code, msg in warnings: + for _fn, lineno, col, _code, msg in problems.get("warnings", []): self.toggleWarning(lineno, col, True, msg, warningType=Editor.WarningCode) self.updateVerticalScrollBar() + @pyqtSlot() def __initOnlineSyntaxCheck(self): """ Private slot to initialize the online syntax check. @@ -6434,6 +6605,7 @@ """ return len(self.notcoveredMarkers) > 0 + @pyqtSlot() def nextUncovered(self): """ Public slot to handle the 'Next uncovered' context menu action. @@ -6451,6 +6623,7 @@ self.setCursorPosition(ucline, 0) self.ensureLineVisible(ucline) + @pyqtSlot() def previousUncovered(self): """ Public slot to handle the 'Previous uncovered' context menu action. @@ -6536,22 +6709,31 @@ ## Syntax error handling methods below ########################################################################### - def toggleSyntaxError(self, line, index, error, msg="", show=False): + def toggleSyntaxError(self, line, index, setError, msg="", show=False): """ Public method to toggle a syntax error indicator. - @param line line number of the syntax error (integer) - @param index index number of the syntax error (integer) - @param error flag indicating if the error marker should be - set or deleted (boolean) - @param msg error message (string) + @param line line number of the syntax error + @type int + @param index index number of the syntax error + @type int + @param setError flag indicating if the error marker should be + set or deleted + @type bool + @param msg error message + @type str @param show flag indicating to set the cursor to the error position - (boolean) + @type bool """ if line == 0: line = 1 # hack to show a syntax error marker, if line is reported to be 0 - if error: + + line = min(line, self.lines()) + # Limit the line number to the ones we really have to ensure proper display + # of the error annotation. + + if setError: # set a new syntax error marker markers = self.markersAtLine(line - 1) index += self.indentation(line - 1) @@ -6560,7 +6742,7 @@ self.syntaxerrors[handle] = [(msg, index)] self.syntaxerrorToggled.emit(self) else: - for handle in list(self.syntaxerrors.keys()): + for handle in self.syntaxerrors: if ( self.markerLine(handle) == line - 1 and (msg, index) not in self.syntaxerrors[handle] @@ -6579,20 +6761,6 @@ self.__setAnnotation(line - 1) self.__markerMap.update() - def getSyntaxErrors(self): - """ - Public method to retrieve the syntax error markers. - - @return sorted list of all lines containing a syntax error - (list of integer) - """ - selist = [] - for handle in list(self.syntaxerrors.keys()): - selist.append(self.markerLine(handle) + 1) - - selist.sort() - return selist - def getSyntaxErrorLines(self): """ Public method to get the lines containing a syntax error. @@ -6617,6 +6785,7 @@ """ return len(self.syntaxerrors) > 0 + @pyqtSlot() def gotoSyntaxError(self): """ Public slot to handle the 'Goto syntax error' context menu action. @@ -6630,6 +6799,7 @@ self.setCursorPosition(seline, index) self.ensureLineVisible(seline) + @pyqtSlot() def clearSyntaxError(self): """ Public slot to handle the 'Clear all syntax error' context menu action. @@ -6641,17 +6811,19 @@ self.syntaxerrors.clear() self.syntaxerrorToggled.emit(self) + @pyqtSlot() def __showSyntaxError(self, line=-1): """ Private slot to handle the 'Show syntax error message' context menu action. - @param line line number to show the syntax error for (integer) + @param line line number to show the syntax error for + @type int """ if line == -1: line = self.line - for handle in list(self.syntaxerrors.keys()): + for handle in self.syntaxerrors: if self.markerLine(handle) == line: errors = [e[0] for e in self.syntaxerrors[handle]] EricMessageBox.critical( @@ -6693,7 +6865,7 @@ ########################################################################### def toggleWarning( - self, line, col, warning, msg="", warningType=WarningCode # noqa: U100 + self, line, col, setWarning, msg="", warningType=WarningCode # noqa: U100 ): """ Public method to toggle a warning indicator. @@ -6701,54 +6873,52 @@ Note: This method is used to set pyflakes and code style warnings. @param line line number of the warning + @type int @param col column of the warning - @param warning flag indicating if the warning marker should be - set or deleted (boolean) - @param msg warning message (string) - @param warningType type of warning message (integer) + @type int + @param setWarning flag indicating if the warning marker should be + set or deleted + @type bool + @param msg warning message + @type str + @param warningType type of warning message + @type int """ if line == 0: line = 1 # hack to show a warning marker, if line is reported to be 0 - if warning: + + line = min(line, self.lines()) + # Limit the line number to the ones we really have to ensure proper display + # of the warning annotation. + + if setWarning: # set/amend a new warning marker warn = (msg, warningType) markers = self.markersAtLine(line - 1) if not (markers & (1 << self.warning)): handle = self.markerAdd(line - 1, self.warning) - self.warnings[handle] = [warn] + self._warnings[handle] = [warn] self.syntaxerrorToggled.emit(self) + # signal is also used for warnings else: - for handle in list(self.warnings.keys()): + for handle in self._warnings: if ( self.markerLine(handle) == line - 1 - and warn not in self.warnings[handle] + and warn not in self._warnings[handle] ): - self.warnings[handle].append(warn) - else: - for handle in list(self.warnings.keys()): + self._warnings[handle].append(warn) + else: + for handle in list(self._warnings.keys()): if self.markerLine(handle) == line - 1: - del self.warnings[handle] + del self._warnings[handle] self.markerDeleteHandle(handle) self.syntaxerrorToggled.emit(self) + # signal is also used for warnings self.__setAnnotation(line - 1) self.__markerMap.update() - def getWarnings(self): - """ - Public method to retrieve the warning markers. - - @return sorted list of all lines containing a warning - (list of integer) - """ - fwlist = [] - for handle in list(self.warnings.keys()): - fwlist.append(self.markerLine(handle) + 1) - - fwlist.sort() - return fwlist - def getWarningLines(self): """ Public method to get the lines containing a warning. @@ -6771,8 +6941,9 @@ @return flag indicating the presence of warnings (boolean) """ - return len(self.warnings) > 0 - + return len(self._warnings) > 0 + + @pyqtSlot() def nextWarning(self): """ Public slot to handle the 'Next warning' context menu action. @@ -6790,6 +6961,7 @@ self.setCursorPosition(fwline, 0) self.ensureLineVisible(fwline) + @pyqtSlot() def previousWarning(self): """ Public slot to handle the 'Previous warning' context menu action. @@ -6807,6 +6979,7 @@ self.setCursorPosition(fwline, 0) self.ensureLineVisible(fwline) + @pyqtSlot() def clearFlakesWarnings(self): """ Public slot to clear all pyflakes warnings. @@ -6814,12 +6987,34 @@ self.__clearTypedWarning(Editor.WarningCode) self.__clearTypedWarning(Editor.WarningPython) + @pyqtSlot() def clearStyleWarnings(self): """ Public slot to clear all style warnings. """ self.__clearTypedWarning(Editor.WarningStyle) + @pyqtSlot() + def clearInfoWarnings(self): + """ + Public slot to clear all info warnings. + """ + self.__clearTypedWarning(Editor.WarningInfo) + + @pyqtSlot() + def clearErrorWarnings(self): + """ + Public slot to clear all error warnings. + """ + self.__clearTypedWarning(Editor.WarningError) + + @pyqtSlot() + def clearCodeWarnings(self): + """ + Public slot to clear all code warnings. + """ + self.__clearTypedWarning(Editor.WarningCode) + def __clearTypedWarning(self, warningKind): """ Private method to clear warnings of a specific kind. @@ -6827,51 +7022,54 @@ @param warningKind kind of warning to clear (Editor.WarningCode, Editor.WarningPython, Editor.WarningStyle) """ - for handle in list(self.warnings.keys()): - warnings = [] - for msg, warningType in self.warnings[handle]: + for handle in list(self._warnings.keys()): + issues = [] + for msg, warningType in self._warnings[handle]: if warningType == warningKind: continue - warnings.append((msg, warningType)) - - if warnings: - self.warnings[handle] = warnings + issues.append((msg, warningType)) + + if issues: + self._warnings[handle] = issues self.__setAnnotation(self.markerLine(handle)) else: - del self.warnings[handle] + del self._warnings[handle] self.__setAnnotation(self.markerLine(handle)) self.markerDeleteHandle(handle) self.syntaxerrorToggled.emit(self) self.__markerMap.update() + @pyqtSlot() def clearWarnings(self): """ Public slot to clear all warnings. """ - for handle in self.warnings: - self.warnings[handle] = [] + for handle in self._warnings: + self._warnings[handle] = [] self.__setAnnotation(self.markerLine(handle)) self.markerDeleteHandle(handle) - self.warnings.clear() + self._warnings.clear() self.syntaxerrorToggled.emit(self) self.__markerMap.update() + @pyqtSlot() def __showWarning(self, line=-1): """ Private slot to handle the 'Show warning' context menu action. - @param line line number to show the warning for (integer) + @param line line number to show the warning for + @type int """ if line == -1: line = self.line - for handle in list(self.warnings.keys()): + for handle in self._warnings: if self.markerLine(handle) == line: EricMessageBox.warning( self, self.tr("Warning"), - "\n".join([w[0] for w in self.warnings[handle]]), + "\n".join([w[0] for w in self._warnings[handle]]), ) break else: @@ -6883,6 +7081,7 @@ ## Annotation handling methods below ########################################################################### + @pyqtSlot() def __setAnnotationStyles(self): """ Private slot to define the style used by inline annotations. @@ -6924,6 +7123,18 @@ Preferences.getEditorColour("AnnotationsStyleBackground"), ) + self.annotationInfoStyle = self.annotationStyleStyle + 1 + self.SendScintilla( + QsciScintilla.SCI_STYLESETFORE, + self.annotationInfoStyle, + Preferences.getEditorColour("AnnotationsInfoForeground"), + ) + self.SendScintilla( + QsciScintilla.SCI_STYLESETBACK, + self.annotationInfoStyle, + Preferences.getEditorColour("AnnotationsInfoBackground"), + ) + def __setAnnotation(self, line): """ Private method to set the annotations for the given line. @@ -6934,12 +7145,17 @@ warningAnnotations = [] errorAnnotations = [] styleAnnotations = [] + infoAnnotations = [] # step 1: do warnings - for handle in self.warnings: + for handle in self._warnings: if self.markerLine(handle) == line: - for msg, warningType in self.warnings[handle]: - if warningType == Editor.WarningStyle: + for msg, warningType in self._warnings[handle]: + if warningType == Editor.WarningInfo: + infoAnnotations.append(self.tr("Info: {0}").format(msg)) + elif warningType == Editor.WarningError: + errorAnnotations.append(self.tr("Error: {0}").format(msg)) + elif warningType == Editor.WarningStyle: styleAnnotations.append(self.tr("Style: {0}").format(msg)) elif warningType == Editor.WarningPython: warningAnnotations.append(msg) @@ -6954,7 +7170,16 @@ for msg, _ in self.syntaxerrors[handle]: errorAnnotations.append(self.tr("Error: {0}").format(msg)) + # step 3: assemble the annotation annotations = [] + if infoAnnotations: + annotationInfoTxt = "\n".join(infoAnnotations) + if styleAnnotations or warningAnnotations or errorAnnotations: + annotationInfoTxt += "\n" + annotations.append( + QsciStyledText(annotationInfoTxt, self.annotationInfoStyle) + ) + if styleAnnotations: annotationStyleTxt = "\n".join(styleAnnotations) if warningAnnotations or errorAnnotations: @@ -6988,7 +7213,10 @@ """ if hasattr(QsciScintilla, "annotate"): self.clearAnnotations() - for handle in list(self.warnings.keys()) + list(self.syntaxerrors.keys()): + for handle in self._warnings: + line = self.markerLine(handle) + self.__setAnnotation(line) + for handle in self.syntaxerrors: line = self.markerLine(handle) self.__setAnnotation(line) @@ -6996,6 +7224,7 @@ ## Fold handling methods ################################################################# + @pyqtSlot() def toggleCurrentFold(self): """ Public slot to toggle the fold containing the current line. @@ -7005,7 +7234,7 @@ def expandFoldWithChildren(self, line=-1): """ - Public slot to expand the current fold including its children. + Public method to expand the current fold including its children. @param line number of line to be expanded @type int @@ -7019,7 +7248,7 @@ def collapseFoldWithChildren(self, line=-1): """ - Public slot to collapse the current fold including its children. + Public method to collapse the current fold including its children. @param line number of line to be expanded @type int @@ -7031,12 +7260,14 @@ QsciScintilla.SCI_FOLDCHILDREN, line, QsciScintilla.SC_FOLDACTION_CONTRACT ) + @pyqtSlot() def __contextMenuExpandFoldWithChildren(self): """ Private slot to handle the context menu expand with children action. """ self.expandFoldWithChildren(self.line) + @pyqtSlot() def __contextMenuCollapseFoldWithChildren(self): """ Private slot to handle the context menu collapse with children action. @@ -7055,7 +7286,7 @@ pressed ok or canceled the operation. (string, boolean) """ qs = [] - for s in list(self.macros.keys()): + for s in self.macros: qs.append(s) qs.sort() return QInputDialog.getItem( @@ -7391,6 +7622,9 @@ @param event the event object @type QFocusEvent """ + if Preferences.getEditor("AutosaveOnFocusLost") and self.__shouldAutosave(): + self.saveFile() + self.vm.editorActGrp.setEnabled(False) self.setCaretWidth(0) @@ -7585,6 +7819,7 @@ self.isLocalFile() and not os.access(self.fileName, os.W_OK) ) or self.isReadOnly() + @pyqtSlot() def refresh(self): """ Public slot to refresh the editor contents. @@ -7603,7 +7838,7 @@ self.clearWarnings() # clear breakpoint markers - for handle in list(self.breaks.keys()): + for handle in self.breaks: self.markerDeleteHandle(handle) self.breaks.clear() @@ -7759,6 +7994,7 @@ return menu + @pyqtSlot() def __showContextMenuResources(self): """ Private slot handling the aboutToShow signal of the resources context @@ -7936,6 +8172,7 @@ ) self.applicationDiagram.show() + @pyqtSlot() def __loadDiagram(self): """ Private slot to load a diagram from file. @@ -7954,6 +8191,7 @@ ## Typing aids related methods below ####################################################################### + @pyqtSlot() def __toggleTypingAids(self): """ Private slot to toggle the typing aids. @@ -8051,6 +8289,7 @@ ## Project related methods ####################################################################### + @pyqtSlot() def __projectPropertiesChanged(self): """ Private slot to handle changes of the project properties. @@ -8080,6 +8319,7 @@ self.project.projectPropertiesChanged.connect(self.__projectPropertiesChanged) + @pyqtSlot() def projectOpened(self): """ Public slot to handle the opening of a project. @@ -8090,6 +8330,7 @@ ) self.setSpellingForProject() + @pyqtSlot() def projectClosed(self): """ Public slot to handle the closing of a project. @@ -8117,7 +8358,7 @@ def __setSpellingLanguage(self, language, pwl="", pel=""): """ - Private slot to set the spell checking language. + Private method to set the spell checking language. @param language spell checking language to be set (string) @param pwl name of the personal/project word list (string) @@ -8217,7 +8458,8 @@ """ Private slot called to handle the user entering a character. - @param charNumber value of the character entered (integer) + @param charNumber value of the character entered + @type int """ if self.spell: if not chr(charNumber).isalnum(): @@ -8225,6 +8467,7 @@ elif self.hasIndicator(self.spellingIndicator, self.currentPosition()): self.spell.checkWord(self.currentPosition()) + @pyqtSlot() def checkSpelling(self): """ Public slot to perform an interactive spell check of the document. @@ -8239,6 +8482,7 @@ if Preferences.getEditor("AutoSpellCheckingEnabled"): self.spell.checkDocumentIncrementally() + @pyqtSlot() def __checkSpellingSelection(self): """ Private slot to spell check the current selection. @@ -8251,6 +8495,7 @@ dlg = SpellCheckingDialog(self.spell, startPos, endPos, self) dlg.exec() + @pyqtSlot() def __checkSpellingWord(self): """ Private slot to check the word below the spelling context menu. @@ -8264,6 +8509,7 @@ dlg = SpellCheckingDialog(self.spell, wordStartPos, wordEndPos, self) dlg.exec() + @pyqtSlot() def __showContextMenuSpelling(self): """ Private slot to set up the spelling menu before it is shown. @@ -8289,12 +8535,14 @@ self.showMenu.emit("Spelling", self.spellingMenu, self) + @pyqtSlot(QAction) def __contextMenuSpellingTriggered(self, action): """ Private slot to handle the selection of a suggestion of the spelling context menu. - @param action reference to the action that was selected (QAction) + @param action reference to the action that was selected + @type QAction """ if action in self.spellingSuggActs: replacement = action.text() @@ -8306,6 +8554,7 @@ self.insert(replacement) self.endUndoAction() + @pyqtSlot() def __addToSpellingDictionary(self): """ Private slot to add the word below the spelling context menu to the @@ -8320,6 +8569,7 @@ if Preferences.getEditor("AutoSpellCheckingEnabled"): self.spell.checkDocumentIncrementally() + @pyqtSlot() def __removeFromSpellingDictionary(self): """ Private slot to remove the word below the context menu to the @@ -8368,7 +8618,7 @@ def shareConnected(self, connected): """ - Public slot to handle a change of the connected state. + Public method to handle a change of the connected state. @param connected flag indicating the connected state (boolean) """ @@ -8382,7 +8632,7 @@ def shareEditor(self, share): """ - Public slot to set the shared status of the editor. + Public method to set the shared status of the editor. @param share flag indicating the share status (boolean) """ @@ -8390,6 +8640,7 @@ if not share: self.shareConnected(False) + @pyqtSlot() def startSharedEdit(self): """ Public slot to start a shared edit session for the editor. @@ -8405,6 +8656,7 @@ ) self.__send(Editor.StartEditToken, hashStr) + @pyqtSlot() def sendSharedEdit(self): """ Public slot to end a shared edit session for the editor and @@ -8417,7 +8669,7 @@ def cancelSharedEdit(self, send=True): """ - Public slot to cancel a shared edit session for the editor. + Public method to cancel a shared edit session for the editor. @param send flag indicating to send the CancelEdit command (boolean) """ @@ -8447,11 +8699,13 @@ self.vm.send(self.fileName, msg) + @pyqtSlot(str) def receive(self, command): """ Public slot to handle received editor commands. - @param command command string (string) + @param command command string + @type str """ if self.__isShared: if self.__isSyncing and not command.startswith( @@ -8479,11 +8733,13 @@ elif token == Editor.SyncToken: self.__processSyncCommand(argsString) + @pyqtSlot(str) def __processStartEditCommand(self, argsString): """ Private slot to process a remote StartEdit command. - @param argsString string containing the command parameters (string) + @param argsString string containing the command parameters + @type str """ if not self.__inSharedEdit and not self.__inRemoteSharedEdit: self.__inRemoteSharedEdit = True @@ -8528,11 +8784,13 @@ return "\n".join(commands) + "\n" + @pyqtSlot(str) def __processEndEditCommand(self, argsString): """ Private slot to process a remote EndEdit command. - @param argsString string containing the command parameters (string) + @param argsString string containing the command parameters + @type str """ commands = argsString.splitlines() sep = self.getLineSeparator() @@ -8567,11 +8825,13 @@ self.setCursorPosition(*cur) + @pyqtSlot(str) def __processRequestSyncCommand(self, argsString): """ Private slot to process a remote RequestSync command. - @param argsString string containing the command parameters (string) + @param argsString string containing the command parameters + @type str """ if self.__inSharedEdit: hashStr = str( @@ -8585,11 +8845,13 @@ if hashStr == argsString: self.__send(Editor.SyncToken, self.__savedText) + @pyqtSlot(str) def __processSyncCommand(self, argsString): """ Private slot to process a remote Sync command. - @param argsString string containing the command parameters (string) + @param argsString string containing the command parameters + @type str """ if self.__isSyncing: cur = self.getCursorPosition() @@ -8614,12 +8876,14 @@ ## Special search related methods ####################################################################### + @pyqtSlot() def searchCurrentWordForward(self): """ Public slot to search the current word forward. """ self.__searchCurrentWord(forward=True) + @pyqtSlot() def searchCurrentWordBackward(self): """ Public slot to search the current word backward. @@ -8666,6 +8930,7 @@ ## Sort related methods ####################################################################### + @pyqtSlot() def sortLines(self): """ Public slot to sort the lines spanned by a rectangular selection. @@ -8870,12 +9135,9 @@ @param name name of the plug-in @type str """ - keys = [] - for key in self.__mouseClickHandlers: + for key in list(self.__mouseClickHandlers.keys()): if self.__mouseClickHandlers[key][0] == name: - keys.append(key) - for key in keys: - del self.__mouseClickHandlers[key] + del self.__mouseClickHandlers[key] def gotoReferenceHandler(self, referencesList): """ @@ -8924,6 +9186,7 @@ ## Methods implementing a Shell interface ####################################################################### + @pyqtSlot() def __executeSelection(self): """ Private slot to execute the selected text in the shell window. @@ -9136,7 +9399,7 @@ def __popupDocstringMenu(self, lastLineText, lastCursorPosition): """ - Private slot to pop up a menu asking the user, if a docstring should be + Private method to pop up a menu asking the user, if a docstring should be inserted. @param lastLineText line contents when the delay timer was started @@ -9145,6 +9408,8 @@ was started (line and index) @type tuple of (int, int) """ + from .DocstringGenerator.BaseDocstringGenerator import DocstringMenuForEnterOnly + cursorPosition = self.getCursorPosition() if lastCursorPosition != cursorPosition: return @@ -9154,10 +9419,6 @@ generator = self.getDocstringGenerator() if generator.hasFunctionDefinition(cursorPosition): - from .DocstringGenerator.BaseDocstringGenerator import ( # __IGNORE_WARNING__ - DocstringMenuForEnterOnly, - ) - docstringMenu = DocstringMenuForEnterOnly(self) act = docstringMenu.addAction( EricPixmapCache.getIcon("fileText"), @@ -9199,6 +9460,7 @@ else: self.__cancelMouseHoverHelp() + @pyqtSlot() def __cancelMouseHoverHelp(self): """ Private slot cancelling the display of mouse hover help.