--- a/src/eric7/QScintilla/Editor.py Mon Nov 13 11:53:55 2023 +0100 +++ b/src/eric7/QScintilla/Editor.py Mon Nov 13 17:38:06 2023 +0100 @@ -317,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 @@ -550,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 = "" @@ -959,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 ) @@ -2097,6 +2103,9 @@ self.undoAvailable.emit(self.isUndoAvailable()) self.redoAvailable.emit(self.isRedoAvailable()) + if not m: + self.__autosaveTimer.stop() + @pyqtSlot(int, int) def __cursorPositionChanged(self, line, index): """ @@ -2113,7 +2122,6 @@ self.cursorChanged.emit(self.fileName, line + 1, index) if Preferences.getEditor("MarkOccurrencesEnabled"): - self.__markOccurrencesTimer.stop() self.__markOccurrencesTimer.start() if self.lastLine != line: @@ -2407,23 +2415,31 @@ @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): """ @@ -3640,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) @@ -4734,7 +4752,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 @@ -4746,7 +4769,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) @@ -6256,21 +6279,32 @@ 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 method 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): """ @@ -6837,6 +6871,8 @@ if not (markers & (1 << self.warning)): handle = self.markerAdd(line - 1, self.warning) self._warnings[handle] = [warn] + self.syntaxerrorToggled.emit(self) + # signal is also used for warnings else: for handle in self._warnings: if ( @@ -6849,6 +6885,8 @@ if self.markerLine(handle) == line - 1: del self._warnings[handle] self.markerDeleteHandle(handle) + self.syntaxerrorToggled.emit(self) + # signal is also used for warnings self.__setAnnotation(line - 1) self.__markerMap.update() @@ -7556,6 +7594,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)