Implement alternative tag display name format

This commit is contained in:
Veronica Berglyd Olsen
2024-01-30 21:34:36 +01:00
parent c5edbbfaf6
commit ca188fee08
7 changed files with 65 additions and 68 deletions
+18 -14
View File
@@ -420,8 +420,7 @@ class NWIndex:
return return
if tBits[0] == nwKeyWords.TAG_KEY: if tBits[0] == nwKeyWords.TAG_KEY:
tagKey = tBits[1] tagKey, displayName = self.parseValue(tBits[1])
displayName = tBits[2] if len(tBits) > 2 else tagKey
self._tagsIndex.add(tagKey, displayName, tHandle, sTitle, itemClass.name) self._tagsIndex.add(tagKey, displayName, tHandle, sTitle, itemClass.name)
self._itemIndex.setHeadingTag(tHandle, sTitle, tagKey) self._itemIndex.setHeadingTag(tHandle, sTitle, tagKey)
tags[tagKey.lower()] = True tags[tagKey.lower()] = True
@@ -472,10 +471,8 @@ class NWIndex:
return True, tBits, tPos return True, tBits, tPos
def checkThese(self, tBits: list[str], nwItem: NWItem) -> list[bool]: def checkThese(self, tBits: list[str], tHandle: str) -> list[bool]:
"""Check the tags against the index to see if they are valid """Check tags against the index to see if they are valid."""
tags. This is needed for syntax highlighting.
"""
nBits = len(tBits) nBits = len(tBits)
isGood = [False]*nBits isGood = [False]*nBits
if nBits == 0: if nBits == 0:
@@ -489,19 +486,25 @@ class NWIndex:
# For a tag, only the first value is accepted, the rest are ignored # For a tag, only the first value is accepted, the rest are ignored
if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1: if tBits[0] == nwKeyWords.TAG_KEY and nBits > 1:
if tBits[1] in self._tagsIndex: if tBits[1] in self._tagsIndex:
isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == nwItem.itemHandle isGood[1] = self._tagsIndex.tagHandle(tBits[1]) == tHandle
else: else:
isGood[1] = True isGood[1] = True
return isGood return isGood
# If we're still here, we check that the references exist # 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 refKey = nwKeyWords.KEY_CLASS[tBits[0]].name
for n in range(1, nBits): for n in range(1, nBits):
if tBits[n] in self._tagsIndex: if (aBit := tBits[n]) in self._tagsIndex:
isGood[n] = self._tagsIndex.tagClass(tBits[n]) == refKey isGood[n] = self._tagsIndex.tagClass(aBit) == refKey and "|" not in aBit
return isGood 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 # Extract Data
## ##
@@ -725,11 +728,12 @@ class TagsIndex:
"""Return a dictionary view of all tags.""" """Return a dictionary view of all tags."""
return self._tags.items() 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.""" """Add a key to the index and set all values."""
self._tags[tagKey.lower()] = { self._tags[tagKey.lower()] = {
"name": tagKey, "name": tagKey,
"display": display, "display": displayName or tagKey,
"handle": tHandle, "handle": tHandle,
"heading": sTitle, "heading": sTitle,
"class": className, "class": className,
@@ -786,9 +790,9 @@ class TagsIndex:
name = entry.get("name") name = entry.get("name")
display = entry.get("display") display = entry.get("display")
handle = entry.get("handle", "") handle = entry.get("handle")
heading = entry.get("heading", "") heading = entry.get("heading")
className = entry.get("class", "") className = entry.get("class")
if not isinstance(name, str): if not isinstance(name, str):
raise ValueError("tagsIndex name is not a string") raise ValueError("tagsIndex name is not a string")
+4 -1
View File
@@ -484,7 +484,10 @@ class ToHtml(Tokenizer):
result = f"<span class='tags'>{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:</span> " result = f"<span class='tags'>{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:</span> "
if len(bits) > 1: if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY: if bits[0] == nwKeyWords.TAG_KEY:
result += f"<a name='tag_{bits[1]}'>{bits[1]}</a>" one, two = self._project.index.parseValue(bits[1])
result += f"<a name='tag_{one}'>{one}</a>"
if two:
result += f" | <span class='optional'>{two}</a>"
else: else:
if self._genMode == self.M_PREVIEW: if self._genMode == self.M_PREVIEW:
result += ", ".join(f"<a href='#{bits[0][1:]}={t}'>{t}</a>" for t in bits[1:]) result += ", ".join(f"<a href='#{bits[0][1:]}={t}'>{t}</a>" for t in bits[1:])
+3 -4
View File
@@ -56,7 +56,6 @@ from novelwriter import CONFIG, SHARED
from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass, nwTrinary
from novelwriter.common import minmax, transferCase from novelwriter.common import minmax, transferCase
from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst from novelwriter.constants import nwKeyWords, nwLabels, nwShortcode, nwUnicode, trConst
from novelwriter.core.item import NWItem
from novelwriter.core.index import countWords from novelwriter.core.index import countWords
from novelwriter.tools.lipsum import GuiLipsum from novelwriter.tools.lipsum import GuiLipsum
from novelwriter.core.document import NWDocument from novelwriter.core.document import NWDocument
@@ -1835,16 +1834,16 @@ class GuiDocEditor(QPlainTextEdit):
if len(text) == 0: if len(text) == 0:
return nwTrinary.NEUTRAL 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) 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 return nwTrinary.NEUTRAL
tag = "" tag = ""
exist = False exist = False
cPos = cursor.selectionStart() - block.position() 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)): for sTag, sPos, sExist in zip(reversed(tBits), reversed(tPos), reversed(tExist)):
if cPos >= sPos: if cPos >= sPos:
# The cursor is between the start of two tags # The cursor is between the start of two tags
+17 -17
View File
@@ -279,23 +279,23 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if text.startswith("@"): # Keywords and commands if text.startswith("@"): # Keywords and commands
self.setCurrentBlockState(self.BLOCK_META) self.setCurrentBlockState(self.BLOCK_META)
if self._tItem: index = SHARED.project.index
pIndex = SHARED.project.index isValid, bits, pos = index.scanThis(text)
isValid, bits, pos = pIndex.scanThis(text) isGood = index.checkThese(bits, self._tHandle)
isGood = pIndex.checkThese(bits, self._tItem) if isValid:
if isValid: for n, bit in enumerate(bits):
for n, bit in enumerate(bits): xPos = pos[n]
xPos = pos[n] xLen = len(bit)
xLen = len(bit) if not isGood[n]:
if isGood[n]: self.setFormat(xPos, xLen, self._hStyles["codeinval"])
if n == 0: elif n == 0:
self.setFormat(xPos, xLen, self._hStyles["keyword"]) self.setFormat(xPos, xLen, self._hStyles["keyword"])
else: else:
self.setFormat(xPos, xLen, self._hStyles["value"]) one, two = index.parseValue(bit)
else: self.setFormat(xPos, len(one), self._hStyles["value"])
kwFmt = self.format(xPos) if two:
kwFmt.merge(self._hStyles["codeinval"]) yPos = xPos + len(bit) - len(two)
self.setFormat(xPos, xLen, kwFmt) self.setFormat(yPos, len(two), self._hStyles["optional"])
# We never want to run the spell checker on keyword/values, # We never want to run the spell checker on keyword/values,
# so we force a return here # so we force a return here
+17 -26
View File
@@ -461,15 +461,16 @@ class GuiDocViewer(QTextBrowser):
def _makeStyleSheet(self) -> None: def _makeStyleSheet(self) -> None:
"""Generate an appropriate style sheet for the document viewer, """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 colText = SHARED.theme.colText
colHead = SHARED.theme.colHead colHead = SHARED.theme.colHead
colVal = SHARED.theme.colVal colVals = SHARED.theme.colVal
colEmph = SHARED.theme.colEmph colEmph = SHARED.theme.colEmph
colKey = SHARED.theme.colKey colKeys = SHARED.theme.colKey
colHidden = SHARED.theme.colHidden colHide = SHARED.theme.colHidden
colMod = SHARED.theme.colMod colMods = SHARED.theme.colMod
colOpts = SHARED.theme.colOpt
styleSheet = ( styleSheet = (
"body {{" "body {{"
" color: rgb({tColR}, {tColG}, {tColB});" " color: rgb({tColR}, {tColG}, {tColB});"
@@ -486,6 +487,9 @@ class GuiDocViewer(QTextBrowser):
".tags {{" ".tags {{"
" color: rgb({kColR}, {kColG}, {kColB});" " color: rgb({kColR}, {kColG}, {kColB});"
"}}\n" "}}\n"
".optional {{"
" color: rgb({oColR}, {oColG}, {oColB});"
"}}\n"
".comment {{" ".comment {{"
" color: rgb({cColR}, {cColG}, {cColB});" " color: rgb({cColR}, {cColG}, {cColB});"
"}}\n" "}}\n"
@@ -496,27 +500,14 @@ class GuiDocViewer(QTextBrowser):
" text-align: center;" " text-align: center;"
"}}\n" "}}\n"
).format( ).format(
tColR=colText.red(), tColR=colText.red(), tColG=colText.green(), tColB=colText.blue(),
tColG=colText.green(), hColR=colHead.red(), hColG=colHead.green(), hColB=colHead.blue(),
tColB=colText.blue(), aColR=colVals.red(), aColG=colVals.green(), aColB=colVals.blue(),
hColR=colHead.red(), eColR=colEmph.red(), eColG=colEmph.green(), eColB=colEmph.blue(),
hColG=colHead.green(), kColR=colKeys.red(), kColG=colKeys.green(), kColB=colKeys.blue(),
hColB=colHead.blue(), cColR=colHide.red(), cColG=colHide.green(), cColB=colHide.blue(),
aColR=colVal.red(), mColR=colMods.red(), mColG=colMods.green(), mColB=colMods.blue(),
aColG=colVal.green(), oColR=colOpts.red(), oColG=colOpts.green(), oColB=colOpts.blue(),
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(),
) )
self.document().setDefaultStyleSheet(styleSheet) self.document().setDefaultStyleSheet(styleSheet)
+3 -3
View File
@@ -1,11 +1,11 @@
%%~name: John Smith %%~name: John Smith
%%~path: f7e2d9f330615/14298de4d9524 %%~path: f7e2d9f330615/14298de4d9524
%%~kind: CHARACTER/NOTE %%~kind: CHARACTER/NOTE
%%~hash: 0f40182de0bb7935bb40fc4c7fd0d75d55421299 %%~hash: f85b4da6ba4a557db0c4d5f1b59f33ba01542606
%%~date: Unknown/2024-01-29 12:19:42 %%~date: Unknown/2024-01-30 10:58:18
# John Smith # John Smith
@tag: John, John Smith @tag: John | John Smith
% Short: The sidekick % Short: The sidekick
+3 -3
View File
@@ -1,11 +1,11 @@
%%~name: Jane Smith %%~name: Jane Smith
%%~path: f7e2d9f330615/bb2c23b3c42cc %%~path: f7e2d9f330615/bb2c23b3c42cc
%%~kind: CHARACTER/NOTE %%~kind: CHARACTER/NOTE
%%~hash: 0faba71c86841552090a9e82dd9b7acd5b0bf56f %%~hash: 10f072a4bf7389b0c178a4181e50833d39b945af
%%~date: Unknown/2024-01-29 12:31:34 %%~date: Unknown/2024-01-30 11:05:37
# Jane Smith # Jane Smith
@tag: Jane, Jane Smith @tag: Jane | Jane Smith
% Short: The heroine % Short: The heroine