Add working meta data blocks
This commit is contained in:
@@ -597,7 +597,10 @@ class Tokenizer(ABC):
|
|||||||
# are automatically skipped.
|
# are automatically skipped.
|
||||||
|
|
||||||
valid, bits, _ = self._project.index.scanThis(aLine)
|
valid, bits, _ = self._project.index.scanThis(aLine)
|
||||||
if valid and bits and bits[0] not in self._skipKeywords:
|
if (
|
||||||
|
valid and bits and bits[0] in nwLabels.KEY_NAME
|
||||||
|
and bits[0] not in self._skipKeywords
|
||||||
|
):
|
||||||
tokens.append((
|
tokens.append((
|
||||||
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
|
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
|
||||||
))
|
))
|
||||||
|
|||||||
+68
-30
@@ -26,12 +26,12 @@ from __future__ import annotations
|
|||||||
import logging
|
import logging
|
||||||
|
|
||||||
from PyQt5.QtGui import (
|
from PyQt5.QtGui import (
|
||||||
QColor, QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat,
|
QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat, QTextCursor,
|
||||||
QTextCursor, QTextDocument
|
QTextDocument
|
||||||
)
|
)
|
||||||
|
|
||||||
from novelwriter import SHARED
|
from novelwriter import SHARED
|
||||||
from novelwriter.constants import nwHeaders
|
from novelwriter.constants import nwHeaders, nwHeadFmt, nwKeyWords, nwLabels, nwUnicode
|
||||||
from novelwriter.core.project import NWProject
|
from novelwriter.core.project import NWProject
|
||||||
from novelwriter.core.tokenizer import T_Formats, Tokenizer
|
from novelwriter.core.tokenizer import T_Formats, Tokenizer
|
||||||
from novelwriter.types import (
|
from novelwriter.types import (
|
||||||
@@ -99,6 +99,24 @@ class ToQTextDocument(Tokenizer):
|
|||||||
self._defaultChar = QTextCharFormat()
|
self._defaultChar = QTextCharFormat()
|
||||||
self._defaultChar.setForeground(SHARED.theme.colText)
|
self._defaultChar.setForeground(SHARED.theme.colText)
|
||||||
|
|
||||||
|
self._comChar = QTextCharFormat()
|
||||||
|
self._comChar.setForeground(SHARED.theme.colHidden)
|
||||||
|
|
||||||
|
self._noteChar = QTextCharFormat()
|
||||||
|
self._noteChar.setForeground(SHARED.theme.colNote)
|
||||||
|
|
||||||
|
self._modChar = QTextCharFormat()
|
||||||
|
self._modChar.setForeground(SHARED.theme.colMod)
|
||||||
|
|
||||||
|
self._keyChar = QTextCharFormat()
|
||||||
|
self._keyChar.setForeground(SHARED.theme.colKey)
|
||||||
|
|
||||||
|
self._tagChar = QTextCharFormat()
|
||||||
|
self._tagChar.setForeground(SHARED.theme.colTag)
|
||||||
|
|
||||||
|
self._optChar = QTextCharFormat()
|
||||||
|
self._optChar.setForeground(SHARED.theme.colOpt)
|
||||||
|
|
||||||
self._defaultBlock = QTextBlockFormat()
|
self._defaultBlock = QTextBlockFormat()
|
||||||
self._defaultBlock.setTopMargin(self._mText[0])
|
self._defaultBlock.setTopMargin(self._mText[0])
|
||||||
self._defaultBlock.setBottomMargin(self._mText[1])
|
self._defaultBlock.setBottomMargin(self._mText[1])
|
||||||
@@ -139,7 +157,7 @@ class ToQTextDocument(Tokenizer):
|
|||||||
else:
|
else:
|
||||||
cursor.setBlockFormat(bFmt)
|
cursor.setBlockFormat(bFmt)
|
||||||
|
|
||||||
for tType, _, tText, tFormat, tStyle in self._tokens:
|
for tType, nHead, tText, tFormat, tStyle in self._tokens:
|
||||||
|
|
||||||
# Styles
|
# Styles
|
||||||
bFmt = QTextBlockFormat(self._defaultBlock)
|
bFmt = QTextBlockFormat(self._defaultBlock)
|
||||||
@@ -173,9 +191,9 @@ class ToQTextDocument(Tokenizer):
|
|||||||
self._insertFragments(tText, tFormat, cursor, self._defaultChar)
|
self._insertFragments(tText, tFormat, cursor, self._defaultChar)
|
||||||
|
|
||||||
elif tType in self.L_HEADINGS:
|
elif tType in self.L_HEADINGS:
|
||||||
bFmt, cFmt = self._genHeadStyle(tType, bFmt)
|
bFmt, cFmt = self._genHeadStyle(tType, nHead, bFmt)
|
||||||
newBlock(bFmt)
|
newBlock(bFmt)
|
||||||
cursor.insertText(tText, cFmt)
|
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)
|
||||||
|
|
||||||
elif tType == self.T_SEP:
|
elif tType == self.T_SEP:
|
||||||
newBlock(bFmt)
|
newBlock(bFmt)
|
||||||
@@ -183,20 +201,30 @@ class ToQTextDocument(Tokenizer):
|
|||||||
|
|
||||||
elif tType == self.T_SKIP:
|
elif tType == self.T_SKIP:
|
||||||
newBlock(bFmt)
|
newBlock(bFmt)
|
||||||
|
cursor.insertText(nwUnicode.U_NBSP, self._defaultChar)
|
||||||
|
|
||||||
elif tType == self.T_SYNOPSIS and self._doSynopsis:
|
elif tType == self.T_SYNOPSIS and self._doSynopsis:
|
||||||
pass
|
newBlock(bFmt)
|
||||||
|
prefix = self._localLookup("Synopsis")
|
||||||
|
cursor.insertText(f"{prefix}: ", self._modChar)
|
||||||
|
self._insertFragments(tText, tFormat, cursor, self._noteChar)
|
||||||
|
|
||||||
elif tType == self.T_SHORT and self._doSynopsis:
|
elif tType == self.T_SHORT and self._doSynopsis:
|
||||||
pass
|
newBlock(bFmt)
|
||||||
|
prefix = self._localLookup("Short Description")
|
||||||
|
cursor.insertText(f"{prefix}: ", self._modChar)
|
||||||
|
self._insertFragments(tText, tFormat, cursor, self._noteChar)
|
||||||
|
|
||||||
elif tType == self.T_COMMENT and self._doComments:
|
elif tType == self.T_COMMENT and self._doComments:
|
||||||
pass
|
newBlock(bFmt)
|
||||||
|
self._insertFragments(tText, tFormat, cursor, self._comChar)
|
||||||
|
|
||||||
elif tType == self.T_KEYWORD and self._doKeywords:
|
elif tType == self.T_KEYWORD and self._doKeywords:
|
||||||
pass
|
newBlock(bFmt)
|
||||||
|
self._insertKeywords(tText, cursor)
|
||||||
|
|
||||||
self._document.blockSignals(False)
|
self._document.blockSignals(False)
|
||||||
|
print(self._document.toHtml())
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -226,16 +254,16 @@ class ToQTextDocument(Tokenizer):
|
|||||||
##
|
##
|
||||||
|
|
||||||
def _insertFragments(
|
def _insertFragments(
|
||||||
self, text: str, tFmt: T_Formats, cursor: QTextCursor,
|
self, text: str, tFmt: T_Formats, cursor: QTextCursor, dFmt: QTextCharFormat
|
||||||
dFmt: QTextCharFormat, bgCol: QColor = QtTransparent
|
|
||||||
) -> None:
|
) -> None:
|
||||||
"""Apply formatting tags to text."""
|
"""Apply formatting tags to text."""
|
||||||
cFmt = QTextCharFormat(dFmt)
|
cFmt = QTextCharFormat(dFmt)
|
||||||
start = 0
|
start = 0
|
||||||
|
temp = text.replace("\n", nwUnicode.U_LSEP)
|
||||||
for pos, fmt, data in tFmt:
|
for pos, fmt, data in tFmt:
|
||||||
|
|
||||||
# Insert buffer with previous format
|
# Insert buffer with previous format
|
||||||
cursor.insertText(text[start:pos], cFmt)
|
cursor.insertText(temp[start:pos], cFmt)
|
||||||
|
|
||||||
# Construct next format
|
# Construct next format
|
||||||
if fmt == self.FMT_B_B:
|
if fmt == self.FMT_B_B:
|
||||||
@@ -257,7 +285,7 @@ class ToQTextDocument(Tokenizer):
|
|||||||
elif fmt == self.FMT_M_B:
|
elif fmt == self.FMT_M_B:
|
||||||
cFmt.setBackground(SHARED.theme.colMark)
|
cFmt.setBackground(SHARED.theme.colMark)
|
||||||
elif fmt == self.FMT_M_E:
|
elif fmt == self.FMT_M_E:
|
||||||
cFmt.setBackground(bgCol)
|
cFmt.setBackground(QtTransparent)
|
||||||
elif fmt == self.FMT_SUP_B:
|
elif fmt == self.FMT_SUP_B:
|
||||||
cFmt.setVerticalAlignment(QtVAlignSuper)
|
cFmt.setVerticalAlignment(QtVAlignSuper)
|
||||||
elif fmt == self.FMT_SUP_E:
|
elif fmt == self.FMT_SUP_E:
|
||||||
@@ -280,27 +308,35 @@ class ToQTextDocument(Tokenizer):
|
|||||||
start = pos
|
start = pos
|
||||||
|
|
||||||
# Insert whatever is left in the buffer
|
# Insert whatever is left in the buffer
|
||||||
cursor.insertText(text[start:], cFmt)
|
cursor.insertText(temp[start:], cFmt)
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def _formatKeywords(self, text: str, style: int) -> str:
|
def _insertKeywords(self, text: str, cursor: QTextCursor) -> None:
|
||||||
"""Apply Markdown formatting to keywords."""
|
"""Apply Markdown formatting to keywords."""
|
||||||
# valid, bits, _ = self._project.index.scanThis("@"+text)
|
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||||
# if not valid or not bits:
|
if valid and bits:
|
||||||
# return ""
|
key = f"{self._localLookup(nwLabels.KEY_NAME[bits[0]])}: "
|
||||||
|
cursor.insertText(key, self._keyChar)
|
||||||
|
if (num := len(bits)) > 1:
|
||||||
|
if bits[0] == nwKeyWords.TAG_KEY:
|
||||||
|
one, two = self._project.index.parseValue(bits[1])
|
||||||
|
cursor.insertText(one, self._tagChar)
|
||||||
|
if two:
|
||||||
|
cursor.insertText(" | ", self._defaultChar)
|
||||||
|
cursor.insertText(two, self._optChar)
|
||||||
|
else:
|
||||||
|
for n, bit in enumerate(bits[1:], 2):
|
||||||
|
cFmt = QTextCharFormat(self._tagChar)
|
||||||
|
cFmt.setFontUnderline(True)
|
||||||
|
cFmt.setAnchor(True)
|
||||||
|
cFmt.setAnchorHref(f"#{bits[0][1:]}={bit}")
|
||||||
|
cursor.insertText(bit, cFmt)
|
||||||
|
if n < num:
|
||||||
|
cursor.insertText(", ", self._defaultChar)
|
||||||
|
return
|
||||||
|
|
||||||
result = ""
|
def _genHeadStyle(self, level: int, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
|
||||||
# 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 & self.A_Z_BTMMRG else "\n\n"
|
|
||||||
|
|
||||||
return result
|
|
||||||
|
|
||||||
def _genHeadStyle(self, level: int, rFmt: QTextBlockFormat) -> T_TextStyle:
|
|
||||||
"""Generate a heading style set."""
|
"""Generate a heading style set."""
|
||||||
mTop, mBottom = self._mHead.get(level, (0.0, 0.0))
|
mTop, mBottom = self._mHead.get(level, (0.0, 0.0))
|
||||||
|
|
||||||
@@ -312,5 +348,7 @@ class ToQTextDocument(Tokenizer):
|
|||||||
cFmt.setForeground(SHARED.theme.colHead)
|
cFmt.setForeground(SHARED.theme.colHead)
|
||||||
cFmt.setFontWeight(QFont.Weight.Bold)
|
cFmt.setFontWeight(QFont.Weight.Bold)
|
||||||
cFmt.setFontPointSize(self._sHead.get(level, 1.0))
|
cFmt.setFontPointSize(self._sHead.get(level, 1.0))
|
||||||
|
cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"])
|
||||||
|
cFmt.setAnchor(True)
|
||||||
|
|
||||||
return bFmt, cFmt
|
return bFmt, cFmt
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ from PyQt5.QtGui import QMouseEvent, QTextCursor
|
|||||||
from PyQt5.QtWidgets import QAction, QApplication, QMenu
|
from PyQt5.QtWidgets import QAction, QApplication, QMenu
|
||||||
|
|
||||||
from novelwriter import CONFIG, SHARED
|
from novelwriter import CONFIG, SHARED
|
||||||
from novelwriter.core.tohtml import ToHtml
|
from novelwriter.core.toqdoc import ToQTextDocument
|
||||||
from novelwriter.enum import nwDocAction
|
from novelwriter.enum import nwDocAction
|
||||||
from novelwriter.gui.docviewer import GuiDocViewer
|
from novelwriter.gui.docviewer import GuiDocViewer
|
||||||
from novelwriter.types import QtModeNone, QtMouseLeft
|
from novelwriter.types import QtModeNone, QtMouseLeft
|
||||||
@@ -119,7 +119,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
# Select All
|
# Select All
|
||||||
assert docViewer.docAction(nwDocAction.SEL_ALL) is True
|
assert docViewer.docAction(nwDocAction.SEL_ALL) is True
|
||||||
cursor = docViewer.textCursor()
|
cursor = docViewer.textCursor()
|
||||||
assert len(cursor.selectedText()) == 3061
|
assert len(cursor.selectedText()) == 3060
|
||||||
|
|
||||||
# Other actions
|
# Other actions
|
||||||
assert docViewer.docAction(nwDocAction.NO_ACTION) is False
|
assert docViewer.docAction(nwDocAction.NO_ACTION) is False
|
||||||
@@ -196,19 +196,19 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
|
|||||||
|
|
||||||
# Document footer show/hide synopsis
|
# Document footer show/hide synopsis
|
||||||
assert nwGUI.viewDocument("f96ec11c6a3da") is True
|
assert nwGUI.viewDocument("f96ec11c6a3da") is True
|
||||||
assert len(docViewer.toPlainText()) == 4315
|
assert len(docViewer.toPlainText()) == 4314
|
||||||
docViewer.docFooter._doToggleSynopsis(False)
|
docViewer.docFooter._doToggleSynopsis(False)
|
||||||
assert len(docViewer.toPlainText()) == 4099
|
assert len(docViewer.toPlainText()) == 4098
|
||||||
|
|
||||||
# Document footer show/hide comments
|
# Document footer show/hide comments
|
||||||
assert nwGUI.viewDocument("846352075de7d") is True
|
assert nwGUI.viewDocument("846352075de7d") is True
|
||||||
assert len(docViewer.toPlainText()) == 675
|
assert len(docViewer.toPlainText()) == 674
|
||||||
docViewer.docFooter._doToggleComments(False)
|
docViewer.docFooter._doToggleComments(False)
|
||||||
assert len(docViewer.toPlainText()) == 635
|
assert len(docViewer.toPlainText()) == 634
|
||||||
|
|
||||||
# Crash the HTML rendering
|
# Crash the HTML rendering
|
||||||
with monkeypatch.context() as mp:
|
with monkeypatch.context() as mp:
|
||||||
mp.setattr(ToHtml, "doConvert", causeException)
|
mp.setattr(ToQTextDocument, "doConvert", causeException)
|
||||||
assert docViewer.loadText("846352075de7d") is False
|
assert docViewer.loadText("846352075de7d") is False
|
||||||
assert docViewer.toPlainText() == "An error occurred while generating the preview."
|
assert docViewer.toPlainText() == "An error occurred while generating the preview."
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user