From 9214da37563350d589b703f0dcae8ac75def2e71 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:53:48 +0200 Subject: [PATCH 1/7] Add mention keyword --- novelwriter/constants.py | 41 ++++++++++++++++++++------------------- novelwriter/core/index.py | 19 +++++++++++------- 2 files changed, 33 insertions(+), 27 deletions(-) diff --git a/novelwriter/constants.py b/novelwriter/constants.py index 9451ea7f..daf09fcb 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -145,34 +145,35 @@ class nwFiles: class nwKeyWords: - TAG_KEY = "@tag" - POV_KEY = "@pov" - FOCUS_KEY = "@focus" - CHAR_KEY = "@char" - PLOT_KEY = "@plot" - TIME_KEY = "@time" - WORLD_KEY = "@location" - OBJECT_KEY = "@object" - ENTITY_KEY = "@entity" - CUSTOM_KEY = "@custom" + TAG_KEY = "@tag" + POV_KEY = "@pov" + FOCUS_KEY = "@focus" + CHAR_KEY = "@char" + PLOT_KEY = "@plot" + TIME_KEY = "@time" + WORLD_KEY = "@location" + OBJECT_KEY = "@object" + ENTITY_KEY = "@entity" + CUSTOM_KEY = "@custom" + MENTION_KEY = "@mention" # Set of Valid Keys VALID_KEYS = { TAG_KEY, POV_KEY, FOCUS_KEY, CHAR_KEY, PLOT_KEY, TIME_KEY, - WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY + WORLD_KEY, OBJECT_KEY, ENTITY_KEY, CUSTOM_KEY, MENTION_KEY } # Map from Keys to Item Class KEY_CLASS = { - POV_KEY: nwItemClass.CHARACTER, - FOCUS_KEY: nwItemClass.CHARACTER, - CHAR_KEY: nwItemClass.CHARACTER, - PLOT_KEY: nwItemClass.PLOT, - TIME_KEY: nwItemClass.TIMELINE, - WORLD_KEY: nwItemClass.WORLD, - OBJECT_KEY: nwItemClass.OBJECT, - ENTITY_KEY: nwItemClass.ENTITY, - CUSTOM_KEY: nwItemClass.CUSTOM, + POV_KEY: nwItemClass.CHARACTER, + FOCUS_KEY: nwItemClass.CHARACTER, + CHAR_KEY: nwItemClass.CHARACTER, + PLOT_KEY: nwItemClass.PLOT, + TIME_KEY: nwItemClass.TIMELINE, + WORLD_KEY: nwItemClass.WORLD, + OBJECT_KEY: nwItemClass.OBJECT, + ENTITY_KEY: nwItemClass.ENTITY, + CUSTOM_KEY: nwItemClass.CUSTOM, } diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 089d4f27..4ca509c0 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -487,13 +487,14 @@ class NWIndex: if nBits == 0: return [] - # Check that the key is valid - isGood[0] = tBits[0] in nwKeyWords.VALID_KEYS + # Check that the keyword is valid + kBit = tBits[0] + isGood[0] = kBit in nwKeyWords.VALID_KEYS if not isGood[0] or nBits == 1: return isGood # For a tag, only the first value is accepted, the rest are ignored - if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1: + if kBit == nwKeyWords.TAG_KEY and nBits > 1: check, _ = self.parseValue(tBits[1]) if check in self._tagsIndex: isGood[1] = self._tagsIndex.tagHandle(check) == tHandle @@ -501,12 +502,16 @@ class NWIndex: isGood[1] = True return isGood + if kBit == nwKeyWords.MENTION_KEY and nBits > 1: + isGood[1:nBits] = [aBit in self._tagsIndex for aBit in tBits[1:nBits]] + return isGood + # If we're still here, we check that the references exist # Class references cannot have the | symbol in them - refKey = nwKeyWords.KEY_CLASS[tBits[0]].name - for n in range(1, nBits): - if (aBit := tBits[n]) in self._tagsIndex: - isGood[n] = self._tagsIndex.tagClass(aBit) == refKey and "|" not in aBit + if rClass := nwKeyWords.KEY_CLASS.get(kBit): + for n in range(1, nBits): + if (aBit := tBits[n]) in self._tagsIndex: + isGood[n] = self._tagsIndex.tagClass(aBit) == rClass.name and "|" not in aBit return isGood From 88aa3f38c281285b6fd606399b1424f5fc98f8cc Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:57:10 +0200 Subject: [PATCH 2/7] Add autocomplete for mentions --- novelwriter/core/index.py | 18 ++++++++++++------ novelwriter/gui/doceditor.py | 2 +- 2 files changed, 13 insertions(+), 7 deletions(-) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index 4ca509c0..f39e0da3 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -686,9 +686,10 @@ class NWIndex: """Return all tags used by a specific document.""" return self._itemIndex.allItemTags(tHandle) if tHandle else [] - def getClassTags(self, itemClass: nwItemClass) -> list[str]: + def getClassTags(self, itemClass: nwItemClass | None) -> list[str]: """Return all tags based on itemClass.""" - return self._tagsIndex.filterTagNames(itemClass.name) + name = None if itemClass is None else itemClass.name + return self._tagsIndex.filterTagNames(name) def getTagsData( self, activeOnly: bool = True @@ -785,11 +786,16 @@ class TagsIndex: """Get the class of a given tag.""" return self._tags.get(tagKey.lower(), {}).get("class", None) - def filterTagNames(self, className: str) -> list[str]: + def filterTagNames(self, className: str | None) -> list[str]: """Get a list of tag names for a given class.""" - return [ - x.get("name", "") for x in self._tags.values() if x.get("class", "") == className - ] + if className is None: + return [ + x.get("name", "") for x in self._tags.values() + ] + else: + return [ + x.get("name", "") for x in self._tags.values() if x.get("class", "") == className + ] ## # Pack/Unpack diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index bedb45da..e6ec14ed 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -2176,7 +2176,7 @@ class MetaCompleter(QMenu): suffix = "" options = list(filter( lambda x: lookup in x.lower(), SHARED.project.index.getClassTags( - nwKeyWords.KEY_CLASS.get(kw.strip(), nwItemClass.NO_CLASS) + nwKeyWords.KEY_CLASS.get(kw.strip()) ) ))[:15] From 46ecb2cf703b4b627dbcb323eb05426cef37a9d6 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:57:39 +0200 Subject: [PATCH 3/7] Add mentions to outline --- novelwriter/constants.py | 56 +++++++++++++++--------------- novelwriter/enum.py | 35 ++++++++++--------- novelwriter/gui/outline.py | 70 ++++++++++++++++++++------------------ 3 files changed, 83 insertions(+), 78 deletions(-) diff --git a/novelwriter/constants.py b/novelwriter/constants.py index daf09fcb..3fa2d7c8 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -237,35 +237,37 @@ class nwLabels: "note": QT_TRANSLATE_NOOP("Constant", "Project Note"), } KEY_NAME = { - nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"), - nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"), - nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"), - nwKeyWords.CHAR_KEY: QT_TRANSLATE_NOOP("Constant", "Characters"), - nwKeyWords.PLOT_KEY: QT_TRANSLATE_NOOP("Constant", "Plot"), - nwKeyWords.TIME_KEY: QT_TRANSLATE_NOOP("Constant", "Timeline"), - nwKeyWords.WORLD_KEY: QT_TRANSLATE_NOOP("Constant", "Locations"), - nwKeyWords.OBJECT_KEY: QT_TRANSLATE_NOOP("Constant", "Objects"), - nwKeyWords.ENTITY_KEY: QT_TRANSLATE_NOOP("Constant", "Entities"), - nwKeyWords.CUSTOM_KEY: QT_TRANSLATE_NOOP("Constant", "Custom"), + nwKeyWords.TAG_KEY: QT_TRANSLATE_NOOP("Constant", "Tag"), + nwKeyWords.POV_KEY: QT_TRANSLATE_NOOP("Constant", "Point of View"), + nwKeyWords.FOCUS_KEY: QT_TRANSLATE_NOOP("Constant", "Focus"), + nwKeyWords.CHAR_KEY: QT_TRANSLATE_NOOP("Constant", "Characters"), + nwKeyWords.PLOT_KEY: QT_TRANSLATE_NOOP("Constant", "Plot"), + nwKeyWords.TIME_KEY: QT_TRANSLATE_NOOP("Constant", "Timeline"), + nwKeyWords.WORLD_KEY: QT_TRANSLATE_NOOP("Constant", "Locations"), + nwKeyWords.OBJECT_KEY: QT_TRANSLATE_NOOP("Constant", "Objects"), + nwKeyWords.ENTITY_KEY: QT_TRANSLATE_NOOP("Constant", "Entities"), + nwKeyWords.CUSTOM_KEY: QT_TRANSLATE_NOOP("Constant", "Custom"), + nwKeyWords.MENTION_KEY: QT_TRANSLATE_NOOP("Constant", "Mentions"), } OUTLINE_COLS = { - nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"), - nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"), - nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"), - nwOutline.LINE: QT_TRANSLATE_NOOP("Constant", "Line"), - nwOutline.CCOUNT: QT_TRANSLATE_NOOP("Constant", "Chars"), - nwOutline.WCOUNT: QT_TRANSLATE_NOOP("Constant", "Words"), - nwOutline.PCOUNT: QT_TRANSLATE_NOOP("Constant", "Pars"), - nwOutline.POV: QT_TRANSLATE_NOOP("Constant", "POV"), - nwOutline.FOCUS: QT_TRANSLATE_NOOP("Constant", "Focus"), - nwOutline.CHAR: KEY_NAME[nwKeyWords.CHAR_KEY], - nwOutline.PLOT: KEY_NAME[nwKeyWords.PLOT_KEY], - nwOutline.WORLD: KEY_NAME[nwKeyWords.WORLD_KEY], - nwOutline.TIME: KEY_NAME[nwKeyWords.TIME_KEY], - nwOutline.OBJECT: KEY_NAME[nwKeyWords.OBJECT_KEY], - nwOutline.ENTITY: KEY_NAME[nwKeyWords.ENTITY_KEY], - nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY], - nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"), + nwOutline.TITLE: QT_TRANSLATE_NOOP("Constant", "Title"), + nwOutline.LEVEL: QT_TRANSLATE_NOOP("Constant", "Level"), + nwOutline.LABEL: QT_TRANSLATE_NOOP("Constant", "Document"), + nwOutline.LINE: QT_TRANSLATE_NOOP("Constant", "Line"), + nwOutline.CCOUNT: QT_TRANSLATE_NOOP("Constant", "Chars"), + nwOutline.WCOUNT: QT_TRANSLATE_NOOP("Constant", "Words"), + nwOutline.PCOUNT: QT_TRANSLATE_NOOP("Constant", "Pars"), + nwOutline.POV: QT_TRANSLATE_NOOP("Constant", "POV"), + nwOutline.FOCUS: QT_TRANSLATE_NOOP("Constant", "Focus"), + nwOutline.CHAR: KEY_NAME[nwKeyWords.CHAR_KEY], + nwOutline.PLOT: KEY_NAME[nwKeyWords.PLOT_KEY], + nwOutline.WORLD: KEY_NAME[nwKeyWords.WORLD_KEY], + nwOutline.TIME: KEY_NAME[nwKeyWords.TIME_KEY], + nwOutline.OBJECT: KEY_NAME[nwKeyWords.OBJECT_KEY], + nwOutline.ENTITY: KEY_NAME[nwKeyWords.ENTITY_KEY], + nwOutline.CUSTOM: KEY_NAME[nwKeyWords.CUSTOM_KEY], + nwOutline.MENTION: KEY_NAME[nwKeyWords.MENTION_KEY], + nwOutline.SYNOP: QT_TRANSLATE_NOOP("Constant", "Synopsis"), } BUILD_FMT = { nwBuildFmt.ODT: QT_TRANSLATE_NOOP("Constant", "Open Document (.odt)"), diff --git a/novelwriter/enum.py b/novelwriter/enum.py index c36e258f..200baa2b 100644 --- a/novelwriter/enum.py +++ b/novelwriter/enum.py @@ -158,23 +158,24 @@ class nwFocus(Enum): class nwOutline(Enum): - TITLE = 0 - LEVEL = 1 - LABEL = 2 - LINE = 3 - CCOUNT = 4 - WCOUNT = 5 - PCOUNT = 6 - POV = 7 - FOCUS = 8 - CHAR = 9 - PLOT = 10 - TIME = 11 - WORLD = 12 - OBJECT = 13 - ENTITY = 14 - CUSTOM = 15 - SYNOP = 16 + TITLE = 0 + LEVEL = 1 + LABEL = 2 + LINE = 3 + CCOUNT = 4 + WCOUNT = 5 + PCOUNT = 6 + POV = 7 + FOCUS = 8 + CHAR = 9 + PLOT = 10 + TIME = 11 + WORLD = 12 + OBJECT = 13 + ENTITY = 14 + CUSTOM = 15 + MENTION = 16 + SYNOP = 17 class nwBuildFmt(Enum): diff --git a/novelwriter/gui/outline.py b/novelwriter/gui/outline.py index fc9b9d48..dac7cc85 100644 --- a/novelwriter/gui/outline.py +++ b/novelwriter/gui/outline.py @@ -312,43 +312,45 @@ class GuiOutlineToolBar(QToolBar): class GuiOutlineTree(QTreeWidget): DEF_WIDTH = { - nwOutline.TITLE: 200, - nwOutline.LEVEL: 40, - nwOutline.LABEL: 150, - nwOutline.LINE: 40, - nwOutline.CCOUNT: 50, - nwOutline.WCOUNT: 50, - nwOutline.PCOUNT: 50, - nwOutline.POV: 100, - nwOutline.FOCUS: 100, - nwOutline.CHAR: 100, - nwOutline.PLOT: 100, - nwOutline.TIME: 100, - nwOutline.WORLD: 100, - nwOutline.OBJECT: 100, - nwOutline.ENTITY: 100, - nwOutline.CUSTOM: 100, - nwOutline.SYNOP: 200, + nwOutline.TITLE: 200, + nwOutline.LEVEL: 40, + nwOutline.LABEL: 150, + nwOutline.LINE: 40, + nwOutline.CCOUNT: 50, + nwOutline.WCOUNT: 50, + nwOutline.PCOUNT: 50, + nwOutline.POV: 100, + nwOutline.FOCUS: 100, + nwOutline.CHAR: 100, + nwOutline.PLOT: 100, + nwOutline.TIME: 100, + nwOutline.WORLD: 100, + nwOutline.OBJECT: 100, + nwOutline.ENTITY: 100, + nwOutline.CUSTOM: 100, + nwOutline.MENTION: 100, + nwOutline.SYNOP: 200, } DEF_HIDDEN = { - nwOutline.TITLE: False, - nwOutline.LEVEL: True, - nwOutline.LABEL: False, - nwOutline.LINE: True, - nwOutline.CCOUNT: True, - nwOutline.WCOUNT: False, - nwOutline.PCOUNT: False, - nwOutline.POV: False, - nwOutline.FOCUS: True, - nwOutline.CHAR: False, - nwOutline.PLOT: False, - nwOutline.TIME: True, - nwOutline.WORLD: False, - nwOutline.OBJECT: True, - nwOutline.ENTITY: True, - nwOutline.CUSTOM: True, - nwOutline.SYNOP: False, + nwOutline.TITLE: False, + nwOutline.LEVEL: True, + nwOutline.LABEL: False, + nwOutline.LINE: True, + nwOutline.CCOUNT: True, + nwOutline.WCOUNT: False, + nwOutline.PCOUNT: False, + nwOutline.POV: False, + nwOutline.FOCUS: True, + nwOutline.CHAR: False, + nwOutline.PLOT: False, + nwOutline.TIME: True, + nwOutline.WORLD: False, + nwOutline.OBJECT: True, + nwOutline.ENTITY: True, + nwOutline.CUSTOM: True, + nwOutline.MENTION: True, + nwOutline.SYNOP: False, } D_HANDLE = QtUserRole From c27343534fdc0a23793c25228557fa1302951938 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Thu, 24 Oct 2024 23:58:37 +0200 Subject: [PATCH 4/7] Add mentions to sample project --- sample/content/636b6aa9b697b.nwd | 5 +++-- sample/nwProject.nwx | 8 ++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd index e1f5955e..c03c1cc2 100644 --- a/sample/content/636b6aa9b697b.nwd +++ b/sample/content/636b6aa9b697b.nwd @@ -1,13 +1,14 @@ %%~name: Making a Scene %%~path: 6a2d6d5f4f401/636b6aa9b697b %%~kind: NOVEL/DOCUMENT -%%~hash: c7e664867218b3a9aac5c12119ef0ec63da2e5cc -%%~date: Unknown/2024-04-18 17:56:30 +%%~hash: 349d9ce3d59ad241d63b01380c53a7fb26ce9f19 +%%~date: Unknown/2024-10-24 23:44:27 ### Making a Scene @pov: Jane @char: John, Jane @location: Earth +@mention: Bob, Space A scene is defined by a level three heading, like the one at the top of this page. The scene will be assigned to the chapter preceding it in the project tree. The scene document can be sorted after the chapter document, or as a child of the chapter. Both result in the same output in the end, so it is a matter of preference. diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 53f3a5e7..3796c43a 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Jane Smith @@ -58,11 +58,11 @@ Chapter One - + Making a Scene - + Another Scene From 9962fd4a55f0228512b3920f6277e2e373dedf5f Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:13:03 +0200 Subject: [PATCH 5/7] Add menu entry and shortcut --- novelwriter/gui/mainmenu.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/novelwriter/gui/mainmenu.py b/novelwriter/gui/mainmenu.py index 3dcdfbcf..28cc7d88 100644 --- a/novelwriter/gui/mainmenu.py +++ b/novelwriter/gui/mainmenu.py @@ -535,16 +535,17 @@ class GuiMainMenu(QMenuBar): # Insert > Tags and References self.mInsKeywords = self.insMenu.addMenu(self.tr("Tags and References")) self.mInsKWItems = {} - self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G") - self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V") - self.mInsKWItems[nwKeyWords.FOCUS_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, F") - self.mInsKWItems[nwKeyWords.CHAR_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, C") - self.mInsKWItems[nwKeyWords.PLOT_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, P") - self.mInsKWItems[nwKeyWords.TIME_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, T") - self.mInsKWItems[nwKeyWords.WORLD_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, L") - self.mInsKWItems[nwKeyWords.OBJECT_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, O") - self.mInsKWItems[nwKeyWords.ENTITY_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, E") - self.mInsKWItems[nwKeyWords.CUSTOM_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, X") + self.mInsKWItems[nwKeyWords.TAG_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, G") + self.mInsKWItems[nwKeyWords.POV_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, V") + self.mInsKWItems[nwKeyWords.FOCUS_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, F") + self.mInsKWItems[nwKeyWords.CHAR_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, C") + self.mInsKWItems[nwKeyWords.PLOT_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, P") + self.mInsKWItems[nwKeyWords.TIME_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, T") + self.mInsKWItems[nwKeyWords.WORLD_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, L") + self.mInsKWItems[nwKeyWords.OBJECT_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, O") + self.mInsKWItems[nwKeyWords.ENTITY_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, E") + self.mInsKWItems[nwKeyWords.CUSTOM_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, X") + self.mInsKWItems[nwKeyWords.MENTION_KEY] = (QAction(self.mInsKeywords), "Ctrl+K, M") for n, keyWord in enumerate(self.mInsKWItems): self.mInsKWItems[keyWord][0].setText(trConst(nwLabels.KEY_NAME[keyWord])) self.mInsKWItems[keyWord][0].setShortcut(self.mInsKWItems[keyWord][1]) From 7f8e2c66a63f8a5007a918d02c5b8471aefee773 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:13:09 +0200 Subject: [PATCH 6/7] Update tests --- tests/test_core/test_core_index.py | 32 +++++++++++++++++++++++++++++- tests/test_gui/test_gui_outline.py | 4 ++-- 2 files changed, 33 insertions(+), 3 deletions(-) diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index 176174bd..b02a3212 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -224,13 +224,17 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): nHandle = project.newFile("Hello", C.hNovelRoot) cHandle = project.newFile("Jane", C.hCharRoot) + wHandle = project.newFile("Earth", C.hWorldRoot) assert isinstance(nHandle, str) assert isinstance(cHandle, str) + assert isinstance(wHandle, str) nItem = project.tree[nHandle] cItem = project.tree[cHandle] + wItem = project.tree[wHandle] assert isinstance(nItem, NWItem) assert isinstance(cItem, NWItem) + assert isinstance(wItem, NWItem) assert index.rootChangedSince(C.hNovelRoot, 0) is False assert index.rootChangedSince(None, 0) is False @@ -242,21 +246,31 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): "@tag:\n" "@:\n" )) + assert index.scanText(wHandle, ( + "# Earth\n" + "@tag: Earth\n" + )) assert index.scanText(nHandle, ( "# Hello World!\n" "@pov: Jane\n" + "@location: Earth\n" "@invalid: John\n" # Checks for issue #688 )) + assert index._tagsIndex.tagHandle("Earth") == wHandle + assert index._tagsIndex.tagHeading("Earth") == "T0001" + assert index._tagsIndex.tagClass("Earth") == "WORLD" + assert index._tagsIndex.tagHandle("Jane") == cHandle assert index._tagsIndex.tagHeading("Jane") == "T0001" assert index._tagsIndex.tagClass("Jane") == "CHARACTER" + assert index.getItemHeading(nHandle, "T0001").title == "Hello World!" # type: ignore assert index.getReferences(nHandle, "T0001") == { "@char": [], "@custom": [], "@entity": [], "@focus": [], - "@location": [], + "@location": ["Earth"], "@object": [], "@plot": [], "@pov": ["Jane"], @@ -283,13 +297,29 @@ def testCoreIndex_CheckThese(mockGUI, fncPath, mockRnd): assert index.checkThese(["@tag", "John"], nHandle) == [True, True] assert index.checkThese(["@pov", "John"], nHandle) == [True, False] assert index.checkThese(["@pov", "Jane"], nHandle) == [True, True] + assert index.checkThese(["@mention", "Jane"], nHandle) == [True, True] assert index.checkThese(["@ pov", "Jane"], nHandle) == [False, False] assert index.checkThese(["@what", "Jane"], nHandle) == [False, False] + # Two w/Class Check + assert index.checkThese(["@pov", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@focus", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@char", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@plot", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@time", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@location", "Earth"], nHandle) == [True, True] + assert index.checkThese(["@object", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@entity", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@custom", "Earth"], nHandle) == [True, False] + assert index.checkThese(["@mention", "Earth"], nHandle) == [True, True] + # Three Items assert index.checkThese(["@tag", "Jane", "John"], cHandle) == [True, True, False] assert index.checkThese(["@who", "Jane", "John"], cHandle) == [False, False, False] assert index.checkThese(["@pov", "Jane", "John"], nHandle) == [True, True, False] + assert index.checkThese(["@pov", "Jane", "Earth"], nHandle) == [True, True, False] + assert index.checkThese(["@mention", "Jane", "Earth"], nHandle) == [True, True, True] + assert index.checkThese(["@mention", "Jane", "Stuff"], nHandle) == [True, True, False] # Parse a Checked Value assert index.parseValue("Jane | Jane Smith") == ("Jane", "Jane Smith") diff --git a/tests/test_gui/test_gui_outline.py b/tests/test_gui/test_gui_outline.py index 12195fa9..9c92820a 100644 --- a/tests/test_gui/test_gui_outline.py +++ b/tests/test_gui/test_gui_outline.py @@ -165,12 +165,12 @@ def testGuiOutline_Main(qtbot, monkeypatch, nwGUI, projPath): # Current Order order = [outlineTree._colIdx[col] for col in outlineTree._treeOrder] - assert order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + assert order == [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] # Move 3 to 0 outlineTree._columnMoved(0, 3, 0) order = [outlineTree._colIdx[col] for col in outlineTree._treeOrder] - assert order == [3, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16] + assert order == [3, 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17] # qtbot.stop() From 9f642d501a53a6eccf042cd2a16d5e0ac6413e19 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 25 Oct 2024 00:20:17 +0200 Subject: [PATCH 7/7] Update documentation --- docs/source/project_overview.rst | 3 +++ docs/source/project_references.rst | 6 ++++++ docs/source/usage_shortcuts.rst | 1 + 3 files changed, 10 insertions(+) diff --git a/docs/source/project_overview.rst b/docs/source/project_overview.rst index 789642d7..6f8d197b 100644 --- a/docs/source/project_overview.rst +++ b/docs/source/project_overview.rst @@ -118,6 +118,9 @@ The root folders are closely tied to the tags and reference system. Each folder the categories of tags that can be used to reference them. For more information about the tags listed, see :ref:`a_references_references`. +There is also a ``@mention`` keyword that can be used to reference any tag. +See :ref:`a_references_references` for more details. + .. note:: You can rename root folders to whatever you want. However, this doesn't change the reference keyword or what they do. diff --git a/docs/source/project_references.rst b/docs/source/project_references.rst index 7b695801..2a0911df 100644 --- a/docs/source/project_references.rst +++ b/docs/source/project_references.rst @@ -162,6 +162,12 @@ reference keywords allow multiple values. Custom references in the current section. The target must be a note tag in a **Custom** type root folder. The custom folder are for any other category of notes you may want to use. +``@mention`` + Anything mentioned, but not present in the current section. It is intended for those cases where + you reveal details about a character or place in a scene without it being otherwise a part of + it. This can be useful when checking for consistency later. Any tag in any root note folder can + be listed under mentions. + The syntax highlighter will alert the user that the tags and references are used correctly, and that the tags referenced exist. diff --git a/docs/source/usage_shortcuts.rst b/docs/source/usage_shortcuts.rst index 420d1456..8aa93e72 100644 --- a/docs/source/usage_shortcuts.rst +++ b/docs/source/usage_shortcuts.rst @@ -165,6 +165,7 @@ a key or key combination for the inserted content. ":kbd:`Ctrl+K`, :kbd:`G`", "Insert a ``@tag`` keyword" ":kbd:`Ctrl+K`, :kbd:`H`", "Insert a short description comment" ":kbd:`Ctrl+K`, :kbd:`L`", "Insert a ``@location`` keyword" + ":kbd:`Ctrl+K`, :kbd:`M`", "Insert a ``@mention`` keyword" ":kbd:`Ctrl+K`, :kbd:`O`", "Insert an ``@object`` keyword" ":kbd:`Ctrl+K`, :kbd:`P`", "Insert a ``@plot`` keyword" ":kbd:`Ctrl+K`, :kbd:`S`", "Insert a synopsis comment"