diff --git a/novelwriter/constants.py b/novelwriter/constants.py index c20b9c47..4fde6f38 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -184,6 +184,10 @@ class nwKeyWords: POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, ] + CAN_LOOKUP: Final[list[str]] = [ + POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, WORLD_KEY, + OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, STORY_KEY, MENTION_KEY, + ] # Set of Valid Keys VALID_KEYS: Final[set[str]] = set(ALL_KEYS) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index fb357842..d8e9c8e4 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -131,12 +131,6 @@ class Index: self._novelExtra = extra return - def setItemClass(self, tHandle: str, itemClass: nwItemClass) -> None: - """Update the class for all tags of a handle.""" - logger.info("Updating class for '%s'", tHandle) - self._tagsIndex.updateClass(tHandle, itemClass.name) - return - ## # Public Methods ## @@ -183,6 +177,16 @@ class Index: self.scanText(tHandle, self._project.storage.getDocumentText(tHandle)) return + def refreshHandle(self, tHandle: str) -> None: + """Update the class for all tags of a handle.""" + if item := self._project.tree[tHandle]: + logger.info("Updating class for '%s'", tHandle) + if item.isInactiveClass(): + self.deleteHandle(tHandle) + else: + self._tagsIndex.updateClass(tHandle, item.itemClass.name) + return + def indexChangedSince(self, checkTime: int | float) -> bool: """Check if the index has changed since a given time.""" return self._indexChange > float(checkTime) @@ -753,10 +757,12 @@ class Index: """Return all tags used by a specific document.""" return self._itemIndex.allItemTags(tHandle) if tHandle else [] - def getClassTags(self, itemClass: nwItemClass | None) -> list[str]: - """Return all tags based on itemClass.""" - name = None if itemClass is None else itemClass.name - return self._tagsIndex.filterTagNames(name) + def getKeyWordTags(self, keyWord: str) -> list[str]: + """Return all tags usable for a specific keyword.""" + if keyWord in nwKeyWords.CAN_LOOKUP: + itemClass = nwKeyWords.KEY_CLASS.get(keyWord) + return self._tagsIndex.filterTagNames(itemClass.name if itemClass else None) + return [] def getTagsData( self, activeOnly: bool = True diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index a8f2fbbd..a6b92734 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -439,7 +439,7 @@ class NWItem: self.setClass(itemClass) if self._type == nwItemType.FILE: # Notify the index of the class change - self._project.index.setItemClass(self._handle, itemClass) + self._project.index.refreshHandle(self._handle) if self._layout == nwItemLayout.NO_LAYOUT: # If no layout is set, pick one diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 0892bb71..846b6317 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -1090,11 +1090,14 @@ class GuiDocEditor(QPlainTextEdit): show = self._completer.updateMetaText(text, bPos) else: show = self._completer.updateCommentText(text, bPos) - point = self.cursorRect().bottomRight() - self._completer.move(viewport.mapToGlobal(point)) - self._completer.setVisible(show) + if show: + point = self.cursorRect().bottomRight() + self._completer.move(viewport.mapToGlobal(point)) + self._completer.show() + else: + self._completer.close() else: - self._completer.setVisible(False) + self._completer.close() if self._doReplace and added == 1: cursor = self.textCursor() @@ -2113,9 +2116,7 @@ class CommandCompleter(QMenu): length = len(lookup) suffix = "" options = sorted(filter( - lambda x: lookup in x.lower(), SHARED.project.index.getClassTags( - nwKeyWords.KEY_CLASS.get(kw.strip()) - ) + lambda x: lookup in x.lower(), SHARED.project.index.getKeyWordTags(kw.strip()) ))[:15] if not options: diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 990a2206..dca4575a 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -95,12 +95,35 @@ def testCoreIndex_LoadSave(qtbot, monkeypatch, prjLipsum, nwGUI, tstPaths): tagIndex = str(index._tagsIndex.packData()) itemsIndex = str(index._itemIndex.packData()) + # Update item class + bHandle = "4c4f28287af27" + bItem = project.tree[bHandle] + assert bItem is not None + + tagsBod = index._tagsIndex["Bod"] + assert tagsBod is not None + assert tagsBod["handle"] == bHandle + assert tagsBod["class"] == "CHARACTER" + + bItem.setClass(nwItemClass.CUSTOM) + index.refreshHandle(bHandle) + assert tagsBod is not None + assert tagsBod["handle"] == bHandle + assert tagsBod["class"] == "CUSTOM" + + # Update item class to inactive + bItem.setClass(nwItemClass.TRASH) + index.refreshHandle(bHandle) + assert "Bod" not in index._tagsIndex + bItem.setClass(nwItemClass.CHARACTER) + index.reIndexHandle(bHandle) + # Delete a handle assert index._tagsIndex["Bod"] is not None - assert index._itemIndex["4c4f28287af27"] is not None - index.deleteHandle("4c4f28287af27") + assert index._itemIndex[bHandle] is not None + index.deleteHandle(bHandle) assert index._tagsIndex["Bod"] is None - assert index._itemIndex["4c4f28287af27"] is None + assert index._itemIndex[bHandle] is None # Clear the index index.clear() @@ -766,11 +789,12 @@ def testCoreIndex_ExtractData(nwGUI, fncPath, mockRnd): assert index.getDocumentTags(cHandle) == ["jane"] assert index.getDocumentTags(None) == [] - # getClassTags - # ============ - assert index.getClassTags(None) == ["Jane", "John"] - assert index.getClassTags(nwItemClass.CHARACTER) == ["Jane", "John"] - assert index.getClassTags(nwItemClass.PLOT) == [] + # getKeyWordTags + # ============== + assert index.getKeyWordTags("@mention") == ["Jane", "John"] + assert index.getKeyWordTags("@char") == ["Jane", "John"] + assert index.getKeyWordTags("@plot") == [] + assert index.getKeyWordTags("@tag") == [] # getTagsData # ===========