Skip to content

Commit

Permalink
Add working meta data blocks
Browse files Browse the repository at this point in the history
  • Loading branch information
vkbo committed May 23, 2024
1 parent c8017a1 commit e998cb6
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 38 deletions.
5 changes: 4 additions & 1 deletion novelwriter/core/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,10 @@ def tokenizeText(self) -> None:
# are automatically skipped.

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((
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
))
Expand Down
98 changes: 68 additions & 30 deletions novelwriter/core/toqdoc.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@
import logging

from PyQt5.QtGui import (
QColor, QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat,
QTextCursor, QTextDocument
QFont, QFontMetrics, QTextBlockFormat, QTextCharFormat, QTextCursor,
QTextDocument
)

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.tokenizer import T_Formats, Tokenizer
from novelwriter.types import (
Expand Down Expand Up @@ -99,6 +99,24 @@ def initDocument(self) -> None:
self._defaultChar = QTextCharFormat()
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.setTopMargin(self._mText[0])
self._defaultBlock.setBottomMargin(self._mText[1])
Expand Down Expand Up @@ -139,7 +157,7 @@ def newBlock(bFmt: QTextBlockFormat) -> None:
else:
cursor.setBlockFormat(bFmt)

for tType, _, tText, tFormat, tStyle in self._tokens:
for tType, nHead, tText, tFormat, tStyle in self._tokens:

# Styles
bFmt = QTextBlockFormat(self._defaultBlock)
Expand Down Expand Up @@ -173,30 +191,40 @@ def newBlock(bFmt: QTextBlockFormat) -> None:
self._insertFragments(tText, tFormat, cursor, self._defaultChar)

elif tType in self.L_HEADINGS:
bFmt, cFmt = self._genHeadStyle(tType, bFmt)
bFmt, cFmt = self._genHeadStyle(tType, nHead, bFmt)
newBlock(bFmt)
cursor.insertText(tText, cFmt)
cursor.insertText(tText.replace(nwHeadFmt.BR, "\n"), cFmt)

elif tType == self.T_SEP:
newBlock(bFmt)
cursor.insertText(tText, self._defaultChar)

elif tType == self.T_SKIP:
newBlock(bFmt)
cursor.insertText(nwUnicode.U_NBSP, self._defaultChar)

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:
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:
pass
newBlock(bFmt)
self._insertFragments(tText, tFormat, cursor, self._comChar)

elif tType == self.T_KEYWORD and self._doKeywords:
pass
newBlock(bFmt)
self._insertKeywords(tText, cursor)

self._document.blockSignals(False)
print(self._document.toHtml())

return

Expand Down Expand Up @@ -226,16 +254,16 @@ def appendFootnotes(self) -> None:
##

def _insertFragments(
self, text: str, tFmt: T_Formats, cursor: QTextCursor,
dFmt: QTextCharFormat, bgCol: QColor = QtTransparent
self, text: str, tFmt: T_Formats, cursor: QTextCursor, dFmt: QTextCharFormat
) -> None:
"""Apply formatting tags to text."""
cFmt = QTextCharFormat(dFmt)
start = 0
temp = text.replace("\n", nwUnicode.U_LSEP)
for pos, fmt, data in tFmt:

# Insert buffer with previous format
cursor.insertText(text[start:pos], cFmt)
cursor.insertText(temp[start:pos], cFmt)

# Construct next format
if fmt == self.FMT_B_B:
Expand All @@ -257,7 +285,7 @@ def _insertFragments(
elif fmt == self.FMT_M_B:
cFmt.setBackground(SHARED.theme.colMark)
elif fmt == self.FMT_M_E:
cFmt.setBackground(bgCol)
cFmt.setBackground(QtTransparent)
elif fmt == self.FMT_SUP_B:
cFmt.setVerticalAlignment(QtVAlignSuper)
elif fmt == self.FMT_SUP_E:
Expand All @@ -280,27 +308,35 @@ def _insertFragments(
start = pos

# Insert whatever is left in the buffer
cursor.insertText(text[start:], cFmt)
cursor.insertText(temp[start:], cFmt)

return

def _formatKeywords(self, text: str, style: int) -> str:
def _insertKeywords(self, text: str, cursor: QTextCursor) -> None:
"""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 & self.A_Z_BTMMRG else "\n\n"

return result
valid, bits, _ = self._project.index.scanThis("@"+text)
if valid and bits:
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

def _genHeadStyle(self, level: int, rFmt: QTextBlockFormat) -> T_TextStyle:
def _genHeadStyle(self, level: int, nHead: int, rFmt: QTextBlockFormat) -> T_TextStyle:
"""Generate a heading style set."""
mTop, mBottom = self._mHead.get(level, (0.0, 0.0))

Expand All @@ -312,5 +348,7 @@ def _genHeadStyle(self, level: int, rFmt: QTextBlockFormat) -> T_TextStyle:
cFmt.setForeground(SHARED.theme.colHead)
cFmt.setFontWeight(QFont.Weight.Bold)
cFmt.setFontPointSize(self._sHead.get(level, 1.0))
cFmt.setAnchorNames([f"{self._handle}:T{nHead:04d}"])
cFmt.setAnchor(True)

return bFmt, cFmt
14 changes: 7 additions & 7 deletions tests/test_gui/test_gui_docviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
from PyQt5.QtWidgets import QAction, QApplication, QMenu

from novelwriter import CONFIG, SHARED
from novelwriter.core.tohtml import ToHtml
from novelwriter.core.toqdoc import ToQTextDocument
from novelwriter.enum import nwDocAction
from novelwriter.gui.docviewer import GuiDocViewer
from novelwriter.types import QtModeNone, QtMouseLeft
Expand Down Expand Up @@ -119,7 +119,7 @@ def testGuiViewer_Main(qtbot, monkeypatch, nwGUI, prjLipsum):
# Select All
assert docViewer.docAction(nwDocAction.SEL_ALL) is True
cursor = docViewer.textCursor()
assert len(cursor.selectedText()) == 3061
assert len(cursor.selectedText()) == 3060

# Other actions
assert docViewer.docAction(nwDocAction.NO_ACTION) is False
Expand Down Expand Up @@ -196,19 +196,19 @@ def mockExec(*a):

# Document footer show/hide synopsis
assert nwGUI.viewDocument("f96ec11c6a3da") is True
assert len(docViewer.toPlainText()) == 4315
assert len(docViewer.toPlainText()) == 4314
docViewer.docFooter._doToggleSynopsis(False)
assert len(docViewer.toPlainText()) == 4099
assert len(docViewer.toPlainText()) == 4098

# Document footer show/hide comments
assert nwGUI.viewDocument("846352075de7d") is True
assert len(docViewer.toPlainText()) == 675
assert len(docViewer.toPlainText()) == 674
docViewer.docFooter._doToggleComments(False)
assert len(docViewer.toPlainText()) == 635
assert len(docViewer.toPlainText()) == 634

# Crash the HTML rendering
with monkeypatch.context() as mp:
mp.setattr(ToHtml, "doConvert", causeException)
mp.setattr(ToQTextDocument, "doConvert", causeException)
assert docViewer.loadText("846352075de7d") is False
assert docViewer.toPlainText() == "An error occurred while generating the preview."

Expand Down

0 comments on commit e998cb6

Please sign in to comment.