Process tags and references in Tokenizer

This commit is contained in:
Veronica Berglyd Olsen
2024-10-22 21:37:18 +02:00
parent c7554f2b8d
commit bd5442c9e5
16 changed files with 589 additions and 466 deletions
+6 -2
View File
@@ -86,8 +86,12 @@ class TextFmt(IntEnum):
SUB_E = 14 # End subscript
COL_B = 15 # Begin colour
COL_E = 16 # End colour
FNOTE = 17 # Footnote marker
STRIP = 18 # Strip the format code
ANM_B = 17 # Begin anchor name
ANM_E = 18 # End anchor name
HRF_B = 19 # Begin href link
HRF_E = 20 # End href link
FNOTE = 21 # Footnote marker
STRIP = 22 # Strip the format code
class BlockTyp(IntEnum):
+3 -26
View File
@@ -38,7 +38,7 @@ from PyQt5.QtGui import QColor
from novelwriter import __version__
from novelwriter.common import firstFloat, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles
from novelwriter.constants import nwHeadFmt, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
@@ -119,12 +119,6 @@ S_META = "MetaText"
S_HEAD = "Header"
S_FNOTE = "FootnoteText"
# Colours
COL_DIALOG_M = "2a6099"
COL_DIALOG_A = "813709"
COL_META_TXT = "813709"
COL_MARK_TXT = "ffffa6"
class DocXXmlFile(NamedTuple):
@@ -292,8 +286,7 @@ class ToDocX(Tokenizer):
self._processFragments(par, S_META, tText, tFormat)
elif tType == BlockTyp.KEYWORD:
tTemp, tFmt = self._formatKeywords(tText)
self._processFragments(par, S_META, tTemp, tFmt)
self._processFragments(par, S_META, tText, tFormat)
return
@@ -369,22 +362,6 @@ class ToDocX(Tokenizer):
# Internal Functions
##
def _formatKeywords(self, text: str) -> tuple[str, T_Formats]:
"""Apply formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits or bits[0] not in nwLabels.KEY_NAME:
return "", []
rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(rTxt) - 1, TextFmt.B_E, "")]
if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
rTxt += bits[1]
else:
rTxt += ", ".join(bits[1:])
return rTxt, rFmt
def _processFragments(
self, par: DocXParagraph, pStyle: str, text: str, tFmt: T_Formats | None = None
) -> None:
@@ -465,7 +442,7 @@ class ToDocX(Tokenizer):
xmlSubElem(rPr, _wTag("u"), attrib={_wTag("val"): "single"})
if fmt & X_MRK:
xmlSubElem(rPr, _wTag("shd"), attrib={
_wTag("fill"): COL_MARK_TXT, _wTag("val"): "clear",
_wTag("fill"): _docXCol(self._theme.highlight), _wTag("val"): "clear",
})
if fmt & X_DEL:
xmlSubElem(rPr, _wTag("strike"))
+16 -34
View File
@@ -30,7 +30,7 @@ from pathlib import Path
from time import time
from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
from novelwriter.constants import nwHeadFmt, nwHtmlUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
from novelwriter.formats.tokenizer import Tokenizer
@@ -48,6 +48,8 @@ HTML_OPENER: dict[int, tuple[int, str]] = {
TextFmt.SUP_B: (TextFmt.SUP_E, "<sup>"),
TextFmt.SUB_B: (TextFmt.SUB_E, "<sub>"),
TextFmt.COL_B: (TextFmt.COL_E, "<span style='color: {0}'>"),
TextFmt.ANM_B: (TextFmt.ANM_E, "<a name='{0}'>"),
TextFmt.HRF_B: (TextFmt.HRF_E, "<a href='{0}'>"),
}
# Each closer tag, with the id of its corresponding opener and tag format
@@ -60,6 +62,8 @@ HTML_CLOSER: dict[int, tuple[int, str]] = {
TextFmt.SUP_E: (TextFmt.SUP_B, "</sup>"),
TextFmt.SUB_E: (TextFmt.SUB_B, "</sub>"),
TextFmt.COL_E: (TextFmt.COL_B, "</span>"),
TextFmt.ANM_E: (TextFmt.ANM_B, "</a>"),
TextFmt.HRF_E: (TextFmt.HRF_B, "</a>"),
}
# Empty HTML tag record
@@ -155,21 +159,21 @@ class ToHtml(Tokenizer):
lines = []
tHandle = self._handle
for tType, nHead, tText, tFormat, tStyle in self._blocks:
for tType, nHead, tText, tFmt, tStyle in self._blocks:
# Replace < and > with HTML entities
if tFormat:
if tFmt:
# If we have formatting, we must recompute the locations
cText = []
i = 0
for c in tText:
if c == "<":
cText.append("&lt;")
tFormat = [(p + 3 if p > i else p, f, k) for p, f, k in tFormat]
tFmt = [(p + 3 if p > i else p, f, k) for p, f, k in tFmt]
i += 4
elif c == ">":
cText.append("&gt;")
tFormat = [(p + 3 if p > i else p, f, k) for p, f, k in tFormat]
tFmt = [(p + 3 if p > i else p, f, k) for p, f, k in tFmt]
i += 4
else:
cText.append(c)
@@ -221,7 +225,7 @@ class ToHtml(Tokenizer):
# Process Text Type
if tType == BlockTyp.TEXT:
lines.append(f"<p{hStyle}>{self._formatText(tText, tFormat)}</p>\n")
lines.append(f"<p{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
elif tType == BlockTyp.TITLE:
tHead = tText.replace(nwHeadFmt.BR, "<br>")
@@ -250,13 +254,10 @@ class ToHtml(Tokenizer):
lines.append(f"<p class='skip'{hStyle}>&nbsp;</p>\n")
elif tType == BlockTyp.COMMENT:
lines.append(f"<p class='comment'>{self._formatText(tText, tFormat)}</p>\n")
lines.append(f"<p class='comment'{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
elif tType == BlockTyp.KEYWORD:
tag, text = self._formatKeywords(tText)
kClass = f" class='meta meta-{tag}'" if tag else ""
tTemp = f"<p{kClass}{hStyle}>{text}</p>\n"
lines.append(tTemp)
lines.append(f"<p class='meta'{hStyle}>{self._formatText(tText, tFmt)}</p>\n")
self._result = "".join(lines)
self._fullHTML.append(self._result)
@@ -431,9 +432,8 @@ class ToHtml(Tokenizer):
mScale, mScale
))
styles.append("a {color: rgb(66, 113, 174);}")
styles.append("mark {background: rgb(255, 255, 166);}")
styles.append(".keyword {color: rgb(245, 135, 31); font-weight: bold;}")
styles.append("a {{color: {0:s};}}".format(self._theme.head.name(QtHexRgb)))
styles.append("mark {{background: {0:s};}}".format(self._theme.highlight.name(QtHexRgb)))
return styles
@@ -455,6 +455,8 @@ class ToHtml(Tokenizer):
if not state.get(fmt, True):
if fmt == TextFmt.COL_B and (color := self._classes.get(data)):
tags.append((pos, m[1].format(color.name(QtHexRgb))))
elif fmt in (TextFmt.ANM_B, TextFmt.HRF_B):
tags.append((pos, m[1].format(data or "#")))
else:
tags.append((pos, m[1]))
state[fmt] = True
@@ -488,23 +490,3 @@ class ToHtml(Tokenizer):
temp = temp.replace("\n", "<br>")
return stripEscape(temp)
def _formatKeywords(self, text: str) -> tuple[str, str]:
"""Apply HTML formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits or bits[0] not in nwLabels.KEY_NAME:
return "", ""
result = f"<span class='keyword'>{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:</span> "
if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
one, two = self._project.index.parseValue(bits[1])
result += f"<a class='tag' name='tag_{one}'>{one}</a>"
if two:
result += f" | <span class='optional'>{two}</a>"
else:
result += ", ".join(
f"<a class='tag' href='#tag_{t}'>{t}</a>" for t in bits[1:]
)
return bits[0][1:], result
+61 -33
View File
@@ -127,10 +127,11 @@ class Tokenizer(ABC):
self._doSynopsis = False # Also process synopsis comments
self._doComments = False # Also process comments
self._doKeywords = False # Also process keywords like tags and references
self._skipKeywords = set() # Keywords to ignore
self._keepBreaks = True # Keep line breaks in paragraphs
self._defaultAlign = "left" # The default text alignment
self._skipKeywords: set[str] = set() # Keywords to ignore
# Other Setting
self._theme = TextDocumentTheme()
self._classes: dict[str, QColor] = {}
@@ -464,6 +465,9 @@ class Tokenizer(ABC):
self._classes["comment"] = self._theme.comment
self._classes["dialog"] = self._theme.dialog
self._classes["altdialog"] = self._theme.altdialog
self._classes["tag"] = self._theme.tag
self._classes["keyword"] = self._theme.keyword
self._classes["optional"] = self._theme.optional
return
def addRootHeading(self, tHandle: str) -> None:
@@ -614,7 +618,9 @@ class Tokenizer(ABC):
if cStyle in (nwComment.SYNOPSIS, nwComment.SHORT, nwComment.PLAIN):
bStyle = COMMENT_STYLE[cStyle]
tLine, tFmt = self._formatComment(bStyle, cKey, cText)
blocks.append((BlockTyp.COMMENT, nHead, tLine, tFmt, sAlign))
blocks.append((
BlockTyp.COMMENT, nHead, tLine, tFmt, sAlign
))
if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
@@ -633,31 +639,14 @@ class Tokenizer(ABC):
if not self._doKeywords:
continue
valid, bits, _ = self._project.index.scanThis(aLine)
if (
valid and bits and bits[0] in nwLabels.KEY_NAME
and bits[0] not in self._skipKeywords
):
tLine, tFmt = self._formatMeta(aLine)
if tLine:
blocks.append((
BlockTyp.KEYWORD, nHead, aLine[1:].strip(), [], sAlign
BlockTyp.KEYWORD, nHead, tLine, tFmt, sAlign
))
if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
# valid, bits, _ = self._project.index.scanThis("@"+text)
# if not valid or not bits or bits[0] not in nwLabels.KEY_NAME:
# return "", []
# rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
# rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(rTxt) - 1, TextFmt.B_E, "")]
# if len(bits) > 1:
# if bits[0] == nwKeyWords.TAG_KEY:
# rTxt += bits[1]
# else:
# rTxt += ", ".join(bits[1:])
# return rTxt, rFmt
elif aLine.startswith(("# ", "#! ")):
# Title or Partition Headings
# ===========================
@@ -1033,22 +1022,12 @@ class Tokenizer(ABC):
allChars += nChars
allWordChars += nWChars
elif tType == BlockTyp.COMMENT:
elif tType in (BlockTyp.COMMENT, BlockTyp.KEYWORD):
words = tText.split()
allWords += len(words)
allChars += len(tText)
allWordChars += len("".join(words))
elif tType == BlockTyp.KEYWORD:
valid, bits, _ = self._project.index.scanThis("@"+tText)
if valid and bits:
key = self._localLookup(nwLabels.KEY_NAME[bits[0]])
text = "{0}: {1}".format(key, ", ".join(bits[1:]))
words = text.split()
allWords += len(words)
allChars += len(text)
allWordChars += len("".join(words))
self._counts["titleCount"] = titleCount
self._counts["paragraphCount"] = paragraphCount
@@ -1112,6 +1091,55 @@ class Tokenizer(ABC):
rFmt.extend((p + shift, f, d) for p, f, d in tFmt)
return tTxt, rFmt
def _formatMeta(self, text: str) -> tuple[str, T_Formats]:
"""Parse a meta line into a """
txt = []
fmt = []
valid, bits, _ = self._project.index.scanThis(text)
if valid and bits and bits[0] in nwLabels.KEY_NAME and bits[0] not in self._skipKeywords:
pos = 0
lbl = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:"
end = len(lbl)
fmt = [
(pos, TextFmt.B_B, ""), (pos, TextFmt.COL_B, "keyword"),
(end, TextFmt.B_E, ""), (end, TextFmt.COL_E, ""),
]
txt = [lbl, " "]
pos = end + 1
if (num := len(bits)) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
one, two = self._project.index.parseValue(bits[1])
end = pos + len(one)
fmt.append((pos, TextFmt.COL_B, "tag"))
fmt.append((pos, TextFmt.ANM_B, f"tag_{one}".lower()))
fmt.append((end, TextFmt.ANM_E, ""))
fmt.append((end, TextFmt.COL_E, ""))
txt.append(one)
pos = end
if two:
txt.append(" | ")
pos += 3
end = pos + len(two)
fmt.append((pos, TextFmt.COL_B, "optional"))
fmt.append((end, TextFmt.COL_E, ""))
txt.append(two)
pos = end
else:
for n, bit in enumerate(bits[1:], 2):
end = pos + len(bit)
fmt.append((pos, TextFmt.COL_B, "tag"))
fmt.append((pos, TextFmt.HRF_B, f"#tag_{bit}".lower()))
fmt.append((end, TextFmt.HRF_E, ""))
fmt.append((end, TextFmt.COL_E, ""))
txt.append(bit)
pos = end
if n < num:
txt.append(", ")
pos += 2
return "".join(txt), fmt
def _extractFormats(
self, text: str, skip: int = 0, hDialog: bool = False
) -> tuple[str, T_Formats]:
+3 -18
View File
@@ -27,7 +27,7 @@ import logging
from pathlib import Path
from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
from novelwriter.constants import nwHeadFmt, nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
@@ -154,7 +154,8 @@ class ToMarkdown(Tokenizer):
lines.append(f"{self._formatText(tText, tFormat, mTags)}\n\n")
elif tType == BlockTyp.KEYWORD:
lines.append(self._formatKeywords(tText, tStyle))
end = " \n" if tStyle & BlockFmt.Z_BTMMRG else "\n\n"
lines.append(f"{self._formatText(tText, tFormat, mTags)}{end}")
self._result = "".join(lines)
self._fullMD.append(self._result)
@@ -215,19 +216,3 @@ class ToMarkdown(Tokenizer):
md = tags.get(fmt, "")
temp = f"{temp[:pos]}{md}{temp[pos:]}"
return temp
def _formatKeywords(self, text: str, style: BlockFmt) -> str:
"""Apply Markdown formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits:
return ""
result = ""
if bits[0] in nwLabels.KEY_NAME:
result += f"**{self._localLookup(nwLabels.KEY_NAME[bits[0]])}:** "
if len(bits) > 1:
result += ", ".join(bits[1:])
result += " \n" if style & BlockFmt.Z_BTMMRG else "\n\n"
return result
+10 -44
View File
@@ -39,11 +39,11 @@ from PyQt5.QtGui import QColor, QFont
from novelwriter import __version__
from novelwriter.common import xmlIndent, xmlSubElem
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles
from novelwriter.constants import nwHeadFmt, nwStyles
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt, stripEscape
from novelwriter.formats.shared import BlockFmt, BlockTyp, TextFmt, stripEscape
from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS
from novelwriter.types import FONT_STYLE, FONT_WEIGHTS, QtHexRgb
logger = logging.getLogger(__name__)
@@ -217,13 +217,6 @@ class ToOdt(Tokenizer):
self._mDocLeft = "2.000cm"
self._mDocRight = "2.000cm"
# Colour
self._colDialogM = "#2a6099"
self._colDialogA = "#813709"
self._colMetaTx = "#813709"
self._opaMetaTx = "100%"
self._markText = "#ffffa6"
return
##
@@ -484,8 +477,7 @@ class ToOdt(Tokenizer):
self._addTextPar(xText, S_META, oStyle, tText, tFmt=tFormat)
elif tType == BlockTyp.KEYWORD:
tTemp, tFmt = self._formatKeywords(tText)
self._addTextPar(xText, S_META, oStyle, tTemp, tFmt=tFmt)
self._addTextPar(xText, S_META, oStyle, tText, tFmt=tFormat)
return
@@ -553,22 +545,6 @@ class ToOdt(Tokenizer):
# Internal Functions
##
def _formatKeywords(self, text: str) -> tuple[str, T_Formats]:
"""Apply formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if not valid or not bits or bits[0] not in nwLabels.KEY_NAME:
return "", []
rTxt = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
rFmt: T_Formats = [(0, TextFmt.B_B, ""), (len(rTxt) - 1, TextFmt.B_E, "")]
if len(bits) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
rTxt += bits[1]
else:
rTxt += ", ".join(bits[1:])
return rTxt, rFmt
def _addTextPar(
self,
xParent: ET.Element,
@@ -598,7 +574,6 @@ class ToOdt(Tokenizer):
parProc = XMLParagraph(xElem)
pErr = 0
xFmt = 0x00
tFrag = ""
fLast = 0
@@ -657,8 +632,6 @@ class ToOdt(Tokenizer):
xNode = self._generateFootnote(fData)
elif fFmt == TextFmt.STRIP:
pass
else:
pErr += 1
fLast = fPos
@@ -671,9 +644,6 @@ class ToOdt(Tokenizer):
else:
parProc.appendSpan(tFrag, self._textStyle(xFmt, fClass))
if pErr > 0:
self._errData.append("Unknown format tag encountered")
nErr, errMsg = parProc.checkError()
if nErr > 0: # pragma: no cover
# This one should only capture bugs
@@ -725,17 +695,13 @@ class ToOdt(Tokenizer):
style.setUnderlineWidth("auto")
style.setUnderlineColor("font-color")
if hFmt & X_MRK:
style.setBackgroundColor(self._markText)
style.setBackgroundColor(self._theme.highlight)
if hFmt & X_SUP:
style.setTextPosition("super")
if hFmt & X_SUB:
style.setTextPosition("sub")
if hFmt & X_COL and color:
style.setColor(color)
# if hFmt & X_DLG:
# style.setColour(self._colDialogM)
# if hFmt & X_DLA:
# style.setColour(self._colDialogA)
self._autoText[tKey] = style
return style.name
@@ -1265,7 +1231,7 @@ class ODTParagraphStyle:
def setColor(self, value: QColor | None) -> None:
"""Set text colour."""
if isinstance(value, QColor):
self._tAttr["color"][1] = value.name(QColor.NameFormat.HexRgb)
self._tAttr["color"][1] = value.name(QtHexRgb)
self._tAttr["opacity"][1] = f"{int(100.0 * value.alphaF())}%"
else:
self._tAttr["color"][1] = None
@@ -1373,15 +1339,15 @@ class ODTTextStyle:
def setColor(self, value: QColor | None) -> None:
"""Set text colour."""
if isinstance(value, QColor):
self._tAttr["color"][1] = value.name(QColor.NameFormat.HexRgb)
self._tAttr["color"][1] = value.name(QtHexRgb)
else:
self._tAttr["color"][1] = None
return
def setBackgroundColor(self, value: str | None) -> None:
def setBackgroundColor(self, value: QColor | None) -> None:
"""Set text background colour."""
if value and len(value) == 7 and value[0] == "#":
self._tAttr["background-color"][1] = value
if isinstance(value, QColor):
self._tAttr["background-color"][1] = value.name(QtHexRgb)
else:
self._tAttr["background-color"][1] = None
return
+36 -68
View File
@@ -34,7 +34,7 @@ from PyQt5.QtGui import (
)
from PyQt5.QtPrintSupport import QPrinter
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwStyles, nwUnicode
from novelwriter.constants import nwHeadFmt, nwStyles, nwUnicode
from novelwriter.core.project import NWProject
from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
from novelwriter.formats.tokenizer import Tokenizer
@@ -146,7 +146,7 @@ class ToQTextDocument(Tokenizer):
self._mIndent = mPx * 2.0
self._tIndent = mPx * self._firstWidth
# Block Format
# Text Formats
# ============
self._blockFmt = QTextBlockFormat()
@@ -157,24 +157,9 @@ class ToQTextDocument(Tokenizer):
100*self._lineHeight, QTextBlockFormat.LineHeightTypes.ProportionalHeight
)
# Character Formats
# =================
self._cText = QTextCharFormat()
self._cText.setBackground(QtTransparent)
self._cText.setForeground(self._theme.text)
self._cCode = QTextCharFormat(self._cText)
self._cCode.setForeground(self._theme.code)
self._cKeyword = QTextCharFormat(self._cText)
self._cKeyword.setForeground(self._theme.keyword)
self._cTag = QTextCharFormat(self._cText)
self._cTag.setForeground(self._theme.tag)
self._cOptional = QTextCharFormat(self._cText)
self._cOptional.setForeground(self._theme.optional)
self._charFmt = QTextCharFormat()
self._charFmt.setBackground(QtTransparent)
self._charFmt.setForeground(self._theme.text)
self._init = True
@@ -193,6 +178,13 @@ class ToQTextDocument(Tokenizer):
# Styles
bFmt = QTextBlockFormat(self._blockFmt)
if tType in (BlockTyp.COMMENT, BlockTyp.KEYWORD):
bFmt.setTopMargin(self._mMeta[0])
bFmt.setBottomMargin(self._mMeta[1])
elif tType == BlockTyp.SEP:
bFmt.setTopMargin(self._mSep[0])
bFmt.setBottomMargin(self._mSep[1])
if tStyle is not None:
if tStyle & BlockFmt.LEFT:
bFmt.setAlignment(QtAlignLeft)
@@ -220,9 +212,9 @@ class ToQTextDocument(Tokenizer):
if tStyle & BlockFmt.IND_T:
bFmt.setTextIndent(self._tIndent)
if tType == BlockTyp.TEXT:
if tType in (BlockTyp.TEXT, BlockTyp.COMMENT, BlockTyp.KEYWORD):
newBlock(cursor, bFmt)
self._insertFragments(tText, tFormat, cursor, self._cText)
self._insertFragments(tText, tFormat, cursor, self._charFmt)
elif tType in self.L_HEADINGS:
bFmt, cFmt = self._genHeadStyle(tType, nHead, bFmt)
@@ -230,23 +222,12 @@ class ToQTextDocument(Tokenizer):
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
elif tType == BlockTyp.SEP:
sFmt = QTextBlockFormat(bFmt)
sFmt.setTopMargin(self._mSep[0])
sFmt.setBottomMargin(self._mSep[1])
newBlock(cursor, sFmt)
cursor.insertText(tText, self._cText)
newBlock(cursor, bFmt)
cursor.insertText(tText, self._charFmt)
elif tType == BlockTyp.SKIP:
newBlock(cursor, bFmt)
cursor.insertText(nwUnicode.U_NBSP, self._cText)
elif tType == BlockTyp.COMMENT:
newBlock(cursor, bFmt)
self._insertFragments(tText, tFormat, cursor, self._cText)
elif tType == BlockTyp.KEYWORD:
newBlock(cursor, bFmt)
self._insertKeywords(tText, cursor)
cursor.insertText(nwUnicode.U_NBSP, self._charFmt)
self._document.blockSignals(False)
@@ -281,12 +262,13 @@ class ToQTextDocument(Tokenizer):
for key, index in self._usedNotes.items():
if content := self._footnotes.get(key):
cFmt = QTextCharFormat(self._cCode)
cFmt = QTextCharFormat(self._charFmt)
cFmt.setForeground(self._theme.code)
cFmt.setAnchor(True)
cFmt.setAnchorNames([f"footnote_{index}"])
newBlock(cursor, self._blockFmt)
cursor.insertText(f"{index}. ", cFmt)
self._insertFragments(*content, cursor, self._cText)
self._insertFragments(*content, cursor, self._charFmt)
self._document.blockSignals(False)
@@ -342,8 +324,21 @@ class ToQTextDocument(Tokenizer):
cFmt.setForeground(color)
elif fmt == TextFmt.COL_E:
cFmt.setForeground(self._theme.text)
elif fmt == TextFmt.ANM_B:
cFmt.setAnchor(True)
cFmt.setAnchorNames([data])
elif fmt == TextFmt.ANM_E:
cFmt.setAnchor(False)
elif fmt == TextFmt.HRF_B:
cFmt.setFontUnderline(True)
cFmt.setAnchor(True)
cFmt.setAnchorHref(data)
elif fmt == TextFmt.HRF_E:
cFmt.setFontUnderline(False)
cFmt.setAnchor(False)
elif fmt == TextFmt.FNOTE:
xFmt = QTextCharFormat(self._cCode)
xFmt = QTextCharFormat(self._charFmt)
xFmt.setForeground(self._theme.code)
xFmt.setVerticalAlignment(QtVAlignSuper)
if data in self._footnotes:
index = len(self._usedNotes) + 1
@@ -363,33 +358,6 @@ class ToQTextDocument(Tokenizer):
return
def _insertKeywords(self, text: str, cursor: QTextCursor) -> None:
"""Apply Markdown formatting to keywords."""
valid, bits, _ = self._project.index.scanThis("@"+text)
if valid and bits:
key = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
cursor.insertText(key, self._cKeyword)
if (num := len(bits)) > 1:
if bits[0] == nwKeyWords.TAG_KEY:
one, two = self._project.index.parseValue(bits[1])
cFmt = QTextCharFormat(self._cTag)
cFmt.setAnchor(True)
cFmt.setAnchorNames([f"tag_{one}".lower()])
cursor.insertText(one, cFmt)
if two:
cursor.insertText(" | ", self._cText)
cursor.insertText(two, self._cOptional)
else:
for n, bit in enumerate(bits[1:], 2):
cFmt = QTextCharFormat(self._cTag)
cFmt.setFontUnderline(True)
cFmt.setAnchor(True)
cFmt.setAnchorHref(f"#tag_{bit}".lower())
cursor.insertText(bit, cFmt)
if n < num:
cursor.insertText(", ", self._cText)
return
def _genHeadStyle(self, hType: BlockTyp, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
"""Generate a heading style set."""
mTop, mBottom = self._mHead.get(hType, (0.0, 0.0))
@@ -398,11 +366,11 @@ class ToQTextDocument(Tokenizer):
bFmt.setTopMargin(mTop)
bFmt.setBottomMargin(mBottom)
self._cTitle = QTextCharFormat(self._cText)
self._cTitle = QTextCharFormat(self._charFmt)
self._cTitle.setFontWeight(self._bold if self._boldHeads else self._normal)
hCol = self._colorHeads and hType != BlockTyp.TITLE
cFmt = QTextCharFormat(self._cText)
cFmt = QTextCharFormat(self._charFmt)
cFmt.setForeground(self._theme.head if hCol else self._theme.text)
cFmt.setFontWeight(self._bold if self._boldHeads else self._normal)
cFmt.setFontPointSize(self._sHead.get(hType, 1.0))