From e74c1057a4e08440d63ce49933503fd5fb65713d Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Fri, 24 May 2024 20:12:13 +0200 Subject: [PATCH] Remove HTML preview mode and manuscript cache --- novelwriter/core/docbuild.py | 20 ++----- novelwriter/core/tohtml.py | 69 +++++------------------ novelwriter/tools/manuscript.py | 87 ++++++++--------------------- tests/test_core/test_core_tohtml.py | 41 -------------- 4 files changed, 41 insertions(+), 176 deletions(-) diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index 9c17e73e..a6d47740 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -55,7 +55,7 @@ class NWBuildDocument: __slots__ = ( "_project", "_build", "_queue", "_error", "_cache", "_count", - "_outline", "_preview" + "_outline", ) def __init__(self, project: NWProject, build: BuildSettings) -> None: @@ -66,7 +66,6 @@ class NWBuildDocument: self._cache = None self._count = False self._outline = False - self._preview = False return ## @@ -100,15 +99,6 @@ class NWBuildDocument: self._outline = state return - def setPreviewMode(self, state: bool) -> None: - """Set the preview mode of the build. This also enables stats - count and outline mode. - """ - self._preview = state - self._outline = state - self._count = state - return - ## # Special Methods ## @@ -140,10 +130,12 @@ class NWBuildDocument: makeObj = ToQTextDocument(self._project) filtered = self._setupBuild(makeObj) + self._outline = True + self._count = True + font = QFont() font.fromString(self._build.getStr("format.textFont")) - makeObj.setLinkHeadings(self._preview) makeObj.initDocument(font, theme) for i, tHandle in enumerate(self._queue): self._error = None @@ -207,8 +199,6 @@ class NWBuildDocument: makeObj = ToHtml(self._project) filtered = self._setupBuild(makeObj) - makeObj.setPreview(self._preview) - makeObj.setLinkHeadings(self._preview) for i, tHandle in enumerate(self._queue): self._error = None if filtered.get(tHandle, (False, 0))[0]: @@ -218,7 +208,7 @@ class NWBuildDocument: makeObj.appendFootnotes() - if not (self._build.getBool("html.preserveTabs") or self._preview): + if not self._build.getBool("html.preserveTabs"): makeObj.replaceTabs() self._error = None diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index f16cc515..33e59789 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -38,24 +38,6 @@ from novelwriter.types import FONT_STYLE, FONT_WEIGHTS logger = logging.getLogger(__name__) -HTML4_TAGS = { - Tokenizer.FMT_B_B: "", - Tokenizer.FMT_B_E: "", - Tokenizer.FMT_I_B: "", - Tokenizer.FMT_I_E: "", - Tokenizer.FMT_D_B: "", - Tokenizer.FMT_D_E: "", - Tokenizer.FMT_U_B: "", - Tokenizer.FMT_U_E: "", - Tokenizer.FMT_M_B: "", - Tokenizer.FMT_M_E: "", - Tokenizer.FMT_SUP_B: "", - Tokenizer.FMT_SUP_E: "", - Tokenizer.FMT_SUB_B: "", - Tokenizer.FMT_SUB_E: "", - Tokenizer.FMT_STRIP: "", -} - HTML5_TAGS = { Tokenizer.FMT_B_B: "", Tokenizer.FMT_B_E: "", @@ -82,14 +64,9 @@ class ToHtml(Tokenizer): also used by the Document Viewer, and Manuscript Build Preview. """ - M_PREVIEW = 0 # Tweak output for the DocViewer - M_EXPORT = 1 # Tweak output for saving to HTML or printing - M_EBOOK = 2 # Tweak output for converting to epub - def __init__(self, project: NWProject) -> None: super().__init__(project) - self._genMode = self.M_EXPORT self._cssStyles = True self._fullHTML: list[str] = [] @@ -112,11 +89,6 @@ class ToHtml(Tokenizer): # Setters ## - def setPreview(self, state: bool) -> None: - """Set to preview generator mode.""" - self._genMode = self.M_PREVIEW if state else self.M_EXPORT - return - def setStyles(self, cssStyles: bool) -> None: """Enable or disable CSS styling. Some elements may still have class tags. @@ -157,8 +129,7 @@ class ToHtml(Tokenizer): """Convert the list of text tokens into an HTML document.""" self._result = "" - hTags = HTML4_TAGS if self._genMode == self.M_PREVIEW else HTML5_TAGS - if self._isNovel and self._genMode != self.M_PREVIEW: + if self._isNovel: # For story files, we bump the titles one level up h1Cl = " class='title'" h1 = "h1" @@ -240,7 +211,7 @@ class ToHtml(Tokenizer): # Process Text Type if tType == self.T_TEXT: - lines.append(f"{self._formatText(tText, tFormat, hTags)}

\n") + lines.append(f"{self._formatText(tText, tFormat)}

\n") elif tType == self.T_TITLE: tHead = tText.replace(nwHeadFmt.BR, "
") @@ -269,13 +240,13 @@ class ToHtml(Tokenizer): lines.append(f"

 

\n") elif tType == self.T_SYNOPSIS and self._doSynopsis: - lines.append(self._formatSynopsis(self._formatText(tText, tFormat, hTags), True)) + lines.append(self._formatSynopsis(self._formatText(tText, tFormat), True)) elif tType == self.T_SHORT and self._doSynopsis: - lines.append(self._formatSynopsis(self._formatText(tText, tFormat, hTags), False)) + lines.append(self._formatSynopsis(self._formatText(tText, tFormat), False)) elif tType == self.T_COMMENT and self._doComments: - lines.append(self._formatComments(self._formatText(tText, tFormat, hTags))) + lines.append(self._formatComments(self._formatText(tText, tFormat))) elif tType == self.T_KEYWORD and self._doKeywords: tag, text = self._formatKeywords(tText) @@ -291,7 +262,6 @@ class ToHtml(Tokenizer): def appendFootnotes(self) -> None: """Append the footnotes in the buffer.""" if self._usedNotes: - tags = HTML4_TAGS if self._genMode == self.M_PREVIEW else HTML5_TAGS footnotes = self._localLookup("Footnotes") lines = [] @@ -299,7 +269,7 @@ class ToHtml(Tokenizer): lines.append("
    \n") for key, index in self._usedNotes.items(): if content := self._footnotes.get(key): - text = self._formatText(*content, tags) + text = self._formatText(*content) lines.append(f"
  1. {text}

  2. \n") lines.append("
\n") @@ -468,7 +438,7 @@ class ToHtml(Tokenizer): # Internal Functions ## - def _formatText(self, text: str, tFmt: T_Formats, tags: dict[int, str]) -> str: + def _formatText(self, text: str, tFmt: T_Formats) -> str: """Apply formatting tags to text.""" temp = text for pos, fmt, data in reversed(tFmt): @@ -481,7 +451,7 @@ class ToHtml(Tokenizer): else: html = "ERR" else: - html = tags.get(fmt, "ERR") + html = HTML5_TAGS.get(fmt, "ERR") temp = f"{temp[:pos]}{html}{temp[pos:]}" temp = temp.replace("\n", "
") return stripEscape(temp) @@ -492,18 +462,12 @@ class ToHtml(Tokenizer): sSynop = self._localLookup("Synopsis") else: sSynop = self._localLookup("Short Description") - if self._genMode == self.M_PREVIEW: - return f"

{sSynop}: {text}

\n" - else: - return f"

{sSynop}: {text}

\n" + return f"

{sSynop}: {text}

\n" def _formatComments(self, text: str) -> str: """Apply HTML formatting to comments.""" - if self._genMode == self.M_PREVIEW: - return f"

{text}

\n" - else: - sComm = self._localLookup("Comment") - return f"

{sComm}: {text}

\n" + sComm = self._localLookup("Comment") + return f"

{sComm}: {text}

\n" def _formatKeywords(self, text: str) -> tuple[str, str]: """Apply HTML formatting to keywords.""" @@ -519,13 +483,8 @@ class ToHtml(Tokenizer): if two: result += f" | {two}" else: - if self._genMode == self.M_PREVIEW: - result += ", ".join( - f"{t}" for t in bits[1:] - ) - else: - result += ", ".join( - f"{t}" for t in bits[1:] - ) + result += ", ".join( + f"{t}" for t in bits[1:] + ) return bits[0][1:], result diff --git a/novelwriter/tools/manuscript.py b/novelwriter/tools/manuscript.py index 84c043dc..d7da4cb0 100644 --- a/novelwriter/tools/manuscript.py +++ b/novelwriter/tools/manuscript.py @@ -23,10 +23,8 @@ along with this program. If not, see . """ from __future__ import annotations -import json import logging -from datetime import datetime from time import time from typing import TYPE_CHECKING @@ -41,12 +39,11 @@ from PyQt5.QtWidgets import ( ) from novelwriter import CONFIG, SHARED -from novelwriter.common import checkInt, fuzzyTime +from novelwriter.common import fuzzyTime from novelwriter.core.buildsettings import BuildCollection, BuildSettings from novelwriter.core.docbuild import NWBuildDocument from novelwriter.core.tokenizer import HeadingFormatter from novelwriter.core.toqdoc import TextDocumentTheme, ToQTextDocument -from novelwriter.error import logException from novelwriter.extensions.circularprogress import NProgressCircle from novelwriter.extensions.modified import NIconToggleButton, NIconToolButton, NToolDialog from novelwriter.gui.theme import STYLES_FLAT_TABS, STYLES_MIN_TOOLBUTTON @@ -250,20 +247,6 @@ class GuiManuscript(NToolDialog): if selected in self._buildMap: self.buildList.setCurrentItem(self._buildMap[selected]) - # logger.debug("Loading build cache") - # cache = CONFIG.dataPath("cache") / f"build_{SHARED.project.data.uuid}.json" - # if cache.is_file(): - # try: - # with open(cache, mode="r", encoding="utf-8") as fObj: - # data = json.load(fObj) - # build = self._builds.getBuild(data.get("uuid", "")) - # if isinstance(build, BuildSettings): - # self._updatePreview(data, build) - # except Exception: - # logger.error("Failed to load build cache") - # logException() - # return - return ## @@ -342,7 +325,6 @@ class GuiManuscript(NToolDialog): SHARED.saveDocument() docBuild = NWBuildDocument(SHARED.project, build) - docBuild.setPreviewMode(True) docBuild.queueAll() theme = TextDocumentTheme() @@ -364,23 +346,17 @@ class GuiManuscript(NToolDialog): buildObj = docBuild.lastBuild assert isinstance(buildObj, ToQTextDocument) - data = { - "uuid": build.buildID, - "time": int(time()), - "stats": buildObj.textStats, - "outline": buildObj.textOutline, - } - self._updatePreview(data, build, buildObj.document) - logger.debug("Saving build cache") - cache = CONFIG.dataPath("cache") / f"build_{SHARED.project.data.uuid}.json" - try: - with open(cache, mode="w+", encoding="utf-8") as outFile: - outFile.write(json.dumps(data, indent=2)) - except Exception: - logger.error("Failed to save build cache") - logException() - return + font = QFont() + font.fromString(build.getStr("format.textFont")) + + self.docPreview.setTextFont(font) + self.docPreview.setContent(buildObj.document) + self.docPreview.setBuildName(build.name) + self.docPreview.setJustify(build.getBool("format.justifyText")) + + self.docStats.updateStats(buildObj.textStats) + self.buildOutline.updateOutline(buildObj.textOutline) return @@ -409,18 +385,6 @@ class GuiManuscript(NToolDialog): # Internal Functions ## - def _updatePreview(self, data: dict, build: BuildSettings, document: QTextDocument) -> None: - """Update the preview widget and set relevant values.""" - font = QFont() - font.fromString(build.getStr("format.textFont")) - self.docPreview.setTextFont(font) - self.docPreview.setContent(data, document) - self.docPreview.setBuildName(build.name) - self.docPreview.setJustify(build.getBool("format.justifyText")) - self.docStats.updateStats(data.get("stats", {})) - self.buildOutline.updateOutline(data.get("outline", {})) - return - def _getSelectedBuild(self) -> BuildSettings | None: """Get the currently selected build. If none are selected, automatically select the first one. @@ -854,23 +818,19 @@ class _PreviewWidget(QTextBrowser): QApplication.processEvents() return - def setContent(self, data: dict, doc: QTextDocument) -> None: + def setContent(self, document: QTextDocument) -> None: """Set the content of the preview widget.""" QApplication.setOverrideCursor(QCursor(Qt.CursorShape.WaitCursor)) self.buildProgress.setCentreText(self.tr("Processing ...")) QApplication.processEvents() - doc.setDocumentMargin(CONFIG.getTextMargin()) - self.setDocument(doc) + document.setDocumentMargin(CONFIG.getTextMargin()) + self.setDocument(document) - self._docTime = checkInt(data.get("time"), 0) + self._docTime = int(time()) self._updateBuildAge() - # Since we change the content while it may still be rendering, we mark - # the document as dirty again to make sure it's re-rendered properly. - # self.document().markContentsDirty(0, self.document().characterCount()) - self.buildProgress.setCentreText(self.tr("Done")) QApplication.restoreOverrideCursor() QApplication.processEvents() @@ -915,17 +875,14 @@ class _PreviewWidget(QTextBrowser): @pyqtSlot() def _updateBuildAge(self) -> None: """Update the build time and the fuzzy age.""" - if self._docTime > 0: - strBuildTime = "%s (%s)" % ( - CONFIG.localDateTime(datetime.fromtimestamp(self._docTime)), - fuzzyTime(int(time()) - self._docTime) - ) + if self._buildName and self._docTime > 0: + self.ageLabel.setText("{0}
{1}: {2}".format( + self._buildName, + self.tr("Built"), + fuzzyTime(int(time()) - self._docTime), + )) else: - strBuildTime = self.tr("Unknown") - text = "{0}: {1}".format(self.tr("Built"), strBuildTime) - if self._buildName: - text = "{0}
{1}".format(self._buildName, text) - self.ageLabel.setText(text) + self.ageLabel.setText("{0}".format(self.tr("No Preview"))) return @pyqtSlot() diff --git a/tests/test_core/test_core_tohtml.py b/tests/test_core/test_core_tohtml.py index c02e833c..d8abf99b 100644 --- a/tests/test_core/test_core_tohtml.py +++ b/tests/test_core/test_core_tohtml.py @@ -284,21 +284,6 @@ def testCoreToHtml_ConvertParagraphs(mockGUI): "\n" ) - # Preview Mode - # ============ - - html.setPreview(True) - - # Text (HTML4) - html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" - html.tokenizeText() - html.doConvert() - assert html.result == ( - "

Some nested bold and italic and " - "strikethrough " - "text here

\n" - ) - @pytest.mark.core def testCoreToHtml_ConvertDirect(mockGUI): @@ -682,29 +667,3 @@ def testCoreToHtml_Format(mockGUI): "Bod, " "Jane" ) - - # Preview Mode - # ============ - - html.setPreview(True) - - assert html._formatSynopsis("synopsis text", True) == ( - "

Synopsis: synopsis text

\n" - ) - assert html._formatSynopsis("short text", False) == ( - "

Short Description: short text

\n" - ) - assert html._formatComments("comment text") == ( - "

comment text

\n" - ) - - assert html._formatKeywords("") == ("", "") - assert html._formatKeywords("tag: Jane") == ( - "tag", "Tag: Jane" - ) - assert html._formatKeywords("char: Bod, Jane") == ( - "char", - "Characters: " - "Bod, " - "Jane" - )