Implement alternative tag display name format
This commit is contained in:
+18
-14
@@ -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")
|
||||
|
||||
@@ -484,7 +484,10 @@ class ToHtml(Tokenizer):
|
||||
result = f"<span class='tags'>{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:</span> "
|
||||
if len(bits) > 1:
|
||||
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:
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
result += ", ".join(f"<a href='#{bits[0][1:]}={t}'>{t}</a>" for t in bits[1:])
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
Reference in New Issue
Block a user