From ca188fee082e69a957062bbe98bceab2d28a1848 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 30 Jan 2024 21:34:36 +0100
Subject: [PATCH] Implement alternative tag display name format
---
novelwriter/core/index.py | 32 +++++++++++++-----------
novelwriter/core/tohtml.py | 5 +++-
novelwriter/gui/doceditor.py | 7 +++---
novelwriter/gui/dochighlight.py | 34 ++++++++++++-------------
novelwriter/gui/docviewer.py | 43 +++++++++++++-------------------
sample/content/14298de4d9524.nwd | 6 ++---
sample/content/bb2c23b3c42cc.nwd | 6 ++---
7 files changed, 65 insertions(+), 68 deletions(-)
diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py
index edea8f71..67d790b1 100644
--- a/novelwriter/core/index.py
+++ b/novelwriter/core/index.py
@@ -420,8 +420,7 @@ class NWIndex:
return
if tBits[0] == nwKeyWords.TAG_KEY:
- tagKey = tBits[1]
- displayName = tBits[2] if len(tBits) > 2 else tagKey
+ tagKey, displayName = self.parseValue(tBits[1])
self._tagsIndex.add(tagKey, displayName, tHandle, sTitle, itemClass.name)
self._itemIndex.setHeadingTag(tHandle, sTitle, tagKey)
tags[tagKey.lower()] = True
@@ -472,10 +471,8 @@ class NWIndex:
return True, tBits, tPos
- def checkThese(self, tBits: list[str], nwItem: NWItem) -> list[bool]:
- """Check the tags against the index to see if they are valid
- tags. This is needed for syntax highlighting.
- """
+ def checkThese(self, tBits: list[str], tHandle: str) -> list[bool]:
+ """Check tags against the index to see if they are valid."""
nBits = len(tBits)
isGood = [False]*nBits
if nBits == 0:
@@ -489,19 +486,25 @@ class NWIndex:
# For a tag, only the first value is accepted, the rest are ignored
if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
if tBits[1] in self._tagsIndex:
- isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == nwItem.itemHandle
+ isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == tHandle
else:
isGood[1] = True
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 tBits[n] in self._tagsIndex:
- isGood[n] = self._tagsIndex.tagClass(tBits[n]) == refKey
+ if (aBit := tBits[n]) in self._tagsIndex:
+ isGood[n] = self._tagsIndex.tagClass(aBit) == refKey and "|" not in aBit
return isGood
+ def parseValue(self, text: str) -> tuple[str, str]:
+ """Parse a single value into a name and display part."""
+ name, _, display = text.partition("|")
+ return name.rstrip(), display.lstrip()
+
##
# Extract Data
##
@@ -725,11 +728,12 @@ class TagsIndex:
"""Return a dictionary view of all tags."""
return self._tags.items()
- def add(self, tagKey: str, display: str, tHandle: str, sTitle: str, className: str) -> None:
+ def add(self, tagKey: str, displayName: str, tHandle: str,
+ sTitle: str, className: str) -> None:
"""Add a key to the index and set all values."""
self._tags[tagKey.lower()] = {
"name": tagKey,
- "display": display,
+ "display": displayName or tagKey,
"handle": tHandle,
"heading": sTitle,
"class": className,
@@ -786,9 +790,9 @@ class TagsIndex:
name = entry.get("name")
display = entry.get("display")
- handle = entry.get("handle", "")
- heading = entry.get("heading", "")
- className = entry.get("class", "")
+ handle = entry.get("handle")
+ heading = entry.get("heading")
+ className = entry.get("class")
if not isinstance(name, str):
raise ValueError("tagsIndex name is not a string")
diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py
index 5598ece7..51e0d04a 100644
--- a/novelwriter/core/tohtml.py
+++ b/novelwriter/core/tohtml.py
@@ -484,7 +484,10 @@ class ToHtml(Tokenizer):
result = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
- result += f"{bits[1]}"
+ one, two = self._project.index.parseValue(bits[1])
+ result += f"{one}"
+ if two:
+ result += f" | {two}"
else:
if self._genMode == self.M_PREVIEW:
result += ", ".join(f"{t}" for t in bits[1:])
diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py
index 6baddf67..b3223aab 100644
--- a/novelwriter/gui/doceditor.py
+++ b/novelwriter/gui/doceditor.py
@@ -56,7 +56,6 @@ from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
-from novelwriter.core.item import NWItem
from novelwriter.core.index import countWords
from novelwriter.tools.lipsum import GuiLipsum
from novelwriter.core.document import NWDocument
@@ -1835,16 +1834,16 @@ class GuiDocEditor(QPlainTextEdit):
if len(text) == 0:
return nwTrinary.NEUTRAL
- if text.startswith("@") and isinstance(self._nwItem, NWItem):
+ if text.startswith("@") and self._docHandle:
isGood, tBits, tPos = SHARED.project.index.scanThis(text)
- if not isGood:
+ if not isGood or not tBits or tBits[0] == nwKeyWords.TAG_KEY:
return nwTrinary.NEUTRAL
tag = ""
exist = False
cPos = cursor.selectionStart() - block.position()
- tExist = SHARED.project.index.checkThese(tBits, self._nwItem)
+ tExist = SHARED.project.index.checkThese(tBits, self._docHandle)
for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
if cPos >= sPos:
# The cursor is between the start of two tags
diff --git a/novelwriter/gui/dochighlight.py b/novelwriter/gui/dochighlight.py
index 01accfda..f292e2f9 100644
--- a/novelwriter/gui/dochighlight.py
+++ b/novelwriter/gui/dochighlight.py
@@ -279,23 +279,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if text.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META)
- if self._tItem:
- pIndex = SHARED.project.index
- isValid, bits, pos = pIndex.scanThis(text)
- isGood = pIndex.checkThese(bits, self._tItem)
- if isValid:
- for n, bit in enumerate(bits):
- xPos = pos[n]
- xLen = len(bit)
- if isGood[n]:
- if n == 0:
- self.setFormat(xPos, xLen, self._hStyles["keyword"])
- else:
- self.setFormat(xPos, xLen, self._hStyles["value"])
- else:
- kwFmt = self.format(xPos)
- kwFmt.merge(self._hStyles["codeinval"])
- self.setFormat(xPos, xLen, kwFmt)
+ index = SHARED.project.index
+ isValid, bits, pos = index.scanThis(text)
+ isGood = index.checkThese(bits, self._tHandle)
+ if isValid:
+ for n, bit in enumerate(bits):
+ xPos = pos[n]
+ xLen = len(bit)
+ if not isGood[n]:
+ self.setFormat(xPos, xLen, self._hStyles["codeinval"])
+ elif n == 0:
+ self.setFormat(xPos, xLen, self._hStyles["keyword"])
+ else:
+ one, two = index.parseValue(bit)
+ self.setFormat(xPos, len(one), self._hStyles["value"])
+ if two:
+ yPos = xPos + len(bit) - len(two)
+ self.setFormat(yPos, len(two), self._hStyles["optional"])
# We never want to run the spell checker on keyword/values,
# so we force a return here
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index 20b78ce7..b6ca8480 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -461,15 +461,16 @@ class GuiDocViewer(QTextBrowser):
def _makeStyleSheet(self) -> None:
"""Generate an appropriate style sheet for the document viewer,
- based on the current syntax highlighter theme,
+ based on the current syntax highlighter theme.
"""
colText = SHARED.theme.colText
colHead = SHARED.theme.colHead
- colVal = SHARED.theme.colVal
+ colVals = SHARED.theme.colVal
colEmph = SHARED.theme.colEmph
- colKey = SHARED.theme.colKey
- colHidden = SHARED.theme.colHidden
- colMod = SHARED.theme.colMod
+ colKeys = SHARED.theme.colKey
+ colHide = SHARED.theme.colHidden
+ colMods = SHARED.theme.colMod
+ colOpts = SHARED.theme.colOpt
styleSheet = (
"body {{"
" color: rgb({tColR}, {tColG}, {tColB});"
@@ -486,6 +487,9 @@ class GuiDocViewer(QTextBrowser):
".tags {{"
" color: rgb({kColR}, {kColG}, {kColB});"
"}}\n"
+ ".optional {{"
+ " color: rgb({oColR}, {oColG}, {oColB});"
+ "}}\n"
".comment {{"
" color: rgb({cColR}, {cColG}, {cColB});"
"}}\n"
@@ -496,27 +500,14 @@ class GuiDocViewer(QTextBrowser):
" text-align: center;"
"}}\n"
).format(
- tColR=colText.red(),
- tColG=colText.green(),
- tColB=colText.blue(),
- hColR=colHead.red(),
- hColG=colHead.green(),
- hColB=colHead.blue(),
- aColR=colVal.red(),
- aColG=colVal.green(),
- aColB=colVal.blue(),
- eColR=colEmph.red(),
- eColG=colEmph.green(),
- eColB=colEmph.blue(),
- kColR=colKey.red(),
- kColG=colKey.green(),
- kColB=colKey.blue(),
- cColR=colHidden.red(),
- cColG=colHidden.green(),
- cColB=colHidden.blue(),
- mColR=colMod.red(),
- mColG=colMod.green(),
- mColB=colMod.blue(),
+ tColR=colText.red(), tColG=colText.green(), tColB=colText.blue(),
+ hColR=colHead.red(), hColG=colHead.green(), hColB=colHead.blue(),
+ aColR=colVals.red(), aColG=colVals.green(), aColB=colVals.blue(),
+ eColR=colEmph.red(), eColG=colEmph.green(), eColB=colEmph.blue(),
+ kColR=colKeys.red(), kColG=colKeys.green(), kColB=colKeys.blue(),
+ cColR=colHide.red(), cColG=colHide.green(), cColB=colHide.blue(),
+ mColR=colMods.red(), mColG=colMods.green(), mColB=colMods.blue(),
+ oColR=colOpts.red(), oColG=colOpts.green(), oColB=colOpts.blue(),
)
self.document().setDefaultStyleSheet(styleSheet)
diff --git a/sample/content/14298de4d9524.nwd b/sample/content/14298de4d9524.nwd
index a0f7992b..b37ae5a8 100644
--- a/sample/content/14298de4d9524.nwd
+++ b/sample/content/14298de4d9524.nwd
@@ -1,11 +1,11 @@
%%~name: John Smith
%%~path: f7e2d9f330615/14298de4d9524
%%~kind: CHARACTER/NOTE
-%%~hash: 0f40182de0bb7935bb40fc4c7fd0d75d55421299
-%%~date: Unknown/2024-01-29 12:19:42
+%%~hash: f85b4da6ba4a557db0c4d5f1b59f33ba01542606
+%%~date: Unknown/2024-01-30 10:58:18
# John Smith
-@tag: John, John Smith
+@tag: John | John Smith
% Short: The sidekick
diff --git a/sample/content/bb2c23b3c42cc.nwd b/sample/content/bb2c23b3c42cc.nwd
index b4abf57b..9c18a616 100644
--- a/sample/content/bb2c23b3c42cc.nwd
+++ b/sample/content/bb2c23b3c42cc.nwd
@@ -1,11 +1,11 @@
%%~name: Jane Smith
%%~path: f7e2d9f330615/bb2c23b3c42cc
%%~kind: CHARACTER/NOTE
-%%~hash: 0faba71c86841552090a9e82dd9b7acd5b0bf56f
-%%~date: Unknown/2024-01-29 12:31:34
+%%~hash: 10f072a4bf7389b0c178a4181e50833d39b945af
+%%~date: Unknown/2024-01-30 11:05:37
# Jane Smith
-@tag: Jane, Jane Smith
+@tag: Jane | Jane Smith
% Short: The heroine