diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index c48dbb58..bdd49925 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -68,7 +68,7 @@ class NWIndex: self._indexBroken = False # TimeStamps - self._indexChange = 0 + self._indexChange = 0.0 self._rootChange = {} return @@ -93,10 +93,22 @@ class NWIndex: """ self._tagsIndex.clear() self._itemIndex.clear() - self._indexChange = 0 + self._indexChange = 0.0 self._rootChange = {} return + def rebuildIndex(self): + """Rebuild the entire index from scratch. + """ + self.clearIndex() + for nwItem in self._project.tree: + if nwItem is not None and nwItem.isFileType(): + tHandle = nwItem.itemHandle + theDoc = self._project.storage.getDocument(tHandle) + self.scanText(tHandle, theDoc.readDocument() or "") + self._indexBroken = False + return + def deleteHandle(self, tHandle): """Delete all entries of a given document handle. """ @@ -125,13 +137,13 @@ class NWIndex: def indexChangedSince(self, checkTime): """Check if the index has changed since a given time. """ - return self._indexChange > checkTime + return self._indexChange > float(checkTime) def rootChangedSince(self, rootHandle, checkTime): """Check if the index has changed since a given time for a given root item. """ - return self._rootChange.get(rootHandle, self._indexChange) > checkTime + return self._rootChange.get(rootHandle, self._indexChange) > float(checkTime) ## # Load and Save Index to/from File @@ -176,7 +188,7 @@ class NWIndex: logger.warning("Item '%s' is not in the index", fHandle) self.reIndexHandle(fHandle) - self._indexChange = round(time()) + self._indexChange = time() logger.debug("Index loaded in %.3f ms", (time() - tStart)*1000) @@ -256,7 +268,7 @@ class NWIndex: self._scanActive(tHandle, theItem, theText, itemTags) # Update timestamps for index changes - nowTime = round(time()) + nowTime = time() self._indexChange = nowTime self._rootChange[theItem.itemRoot] = nowTime diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index cdcb83d0..bb26ff5e 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -369,8 +369,11 @@ class NWProject(QObject): self._scanProjectFolder() self._index.loadIndex() - self.updateWordCounts() + if xmlReader.state == XMLReadState.WAS_LEGACY: + # Often, the index needs to be rebuilt when updating format + self._index.rebuildIndex() + self.updateWordCounts() self._projOpened = time() self._projAltered = False diff --git a/novelwriter/guimain.py b/novelwriter/guimain.py index 20f7ce2d..56ba564f 100644 --- a/novelwriter/guimain.py +++ b/novelwriter/guimain.py @@ -833,17 +833,8 @@ class GuiMain(QMainWindow): tStart = time() self.projView.saveProjectTasks() - self.theProject.index.clearIndex() - - for tItem in self.theProject.tree: - if tItem is None: # pragma: no cover - continue # This is a bug trap - - logger.debug("Indexing '%s'", tItem.itemName) - if self.theProject.index.reIndexHandle(tItem.itemHandle): - # Update Word Counts - self.projView.propagateCount(tItem.itemHandle, tItem.wordCount, countChildren=True) - self.projView.setTreeItemValues(tItem.itemHandle) + self.theProject.index.rebuildIndex() + self.projView.populateTree() tEnd = time() self.setStatus( diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 82bf53c8..6d85dfb0 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -90,7 +90,7 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths): assert theIndex._tagsIndex._tags == {} assert theIndex._itemIndex._items == {} - # No folder for sloading + # No folder for loading with monkeypatch.context() as mp: mp.setattr("novelwriter.core.storage.NWStorage.getMetaFile", lambda *a: None) assert theIndex.loadIndex() is False @@ -108,6 +108,13 @@ def testCoreIndex_LoadSave(monkeypatch, prjLipsum, mockGUI, tstPaths): assert str(theIndex._tagsIndex.packData()) == tagIndex assert str(theIndex._itemIndex.packData()) == itemsIndex + # Rebuild index + theIndex.clearIndex() + theIndex.rebuildIndex() + + assert str(theIndex._tagsIndex.packData()) == tagIndex + assert str(theIndex._itemIndex.packData()) == itemsIndex + # Check File copyfile(projFile, testFile) assert cmpFiles(testFile, compFile) diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index d2931fd8..1f3dc73e 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -230,7 +230,7 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd): assert "The file format of your project is about to be" in mockGUI.lastQuestion[1] mockGUI.askResponse = True - # Won't convert legacy file + # Won't open project from newer version with monkeypatch.context() as mp: mp.setattr(ProjectXMLReader, "hexVersion", property(lambda *a: 0x99999999)) mockGUI.askResponse = False @@ -245,6 +245,18 @@ def testCoreProject_Open(monkeypatch, caplog, mockGUI, fncPath, mockRnd): assert theProject.closeProject() + # Trigger an index rebuild + with monkeypatch.context() as mp: + mp.setattr(ProjectXMLReader, "state", property(lambda *a: XMLReadState.WAS_LEGACY)) + mp.setattr("novelwriter.core.index.NWIndex.loadIndex", lambda *a: True) + mockGUI.askResponse = True + theProject.index._indexBroken = True + assert theProject.openProject(fncPath) is True + assert "The file format of your project is about to be" in mockGUI.lastQuestion[1] + assert theProject.index._indexBroken is False + + assert theProject.closeProject() + # END Test testCoreProject_Open