diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py
index 7fce8e92..360ae8ed 100644
--- a/novelwriter/core/docbuild.py
+++ b/novelwriter/core/docbuild.py
@@ -42,6 +42,7 @@ from novelwriter.formats.tokenizer import Tokenizer
from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt
from novelwriter.formats.toqdoc import TextDocumentTheme, ToQTextDocument
+from novelwriter.formats.toraw import ToRaw
logger = logging.getLogger(__name__)
@@ -146,24 +147,67 @@ class NWBuildDocument:
return
- def iterBuild(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
+ def iterBuildDocument(self, path: Path, bFormat: nwBuildFmt) -> Iterable[tuple[int, bool]]:
"""Wrapper for builders based on format."""
+ if bFormat in (nwBuildFmt.J_HTML, nwBuildFmt.J_NWD):
+ # Ensure that JSON output has the correct extension
+ path = path.with_suffix(".json")
+
if bFormat in (nwBuildFmt.ODT, nwBuildFmt.FODT):
- yield from self.iterBuildOpenDocument(path, bFormat == nwBuildFmt.FODT)
+ makeObj = ToOdt(self._project, bFormat == nwBuildFmt.FODT)
+ filtered = self._setupBuild(makeObj)
+ makeObj.initDocument()
+
+ yield from self._iterBuild(makeObj, filtered)
+
+ makeObj.closeDocument()
+
elif bFormat in (nwBuildFmt.HTML, nwBuildFmt.J_HTML):
- yield from self.iterBuildHTML(path, asJson=bFormat == nwBuildFmt.J_HTML)
+ makeObj = ToHtml(self._project)
+ filtered = self._setupBuild(makeObj)
+
+ yield from self._iterBuild(makeObj, filtered)
+
+ makeObj.appendFootnotes()
+ if not self._build.getBool("html.preserveTabs"):
+ makeObj.replaceTabs()
+
elif bFormat in (nwBuildFmt.STD_MD, nwBuildFmt.EXT_MD):
- yield from self.iterBuildMarkdown(path, bFormat == nwBuildFmt.EXT_MD)
+ makeObj = ToMarkdown(self._project, bFormat == nwBuildFmt.EXT_MD)
+ filtered = self._setupBuild(makeObj)
+
+ yield from self._iterBuild(makeObj, filtered)
+
+ makeObj.appendFootnotes()
+ if self._build.getBool("format.replaceTabs"):
+ makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
+
elif bFormat in (nwBuildFmt.NWD, nwBuildFmt.J_NWD):
- yield from self.iterBuildNWD(path, asJson=bFormat == nwBuildFmt.J_NWD)
+ makeObj = ToRaw(self._project)
+ filtered = self._setupBuild(makeObj)
+
+ yield from self._iterBuild(makeObj, filtered)
+
+ if self._build.getBool("format.replaceTabs"):
+ makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
+
+ self._error = None
+ self._cache = makeObj
+
+ try:
+ makeObj.saveDocument(path)
+ except Exception as exc:
+ logException()
+ self._error = formatException(exc)
+
return
- def iterBuildOpenDocument(self, path: Path, isFlat: bool) -> Iterable[tuple[int, bool]]:
- """Build an Open Document file."""
- makeObj = ToOdt(self._project, isFlat=isFlat)
- filtered = self._setupBuild(makeObj)
- makeObj.initDocument()
+ ##
+ # Internal Functions
+ ##
+ def _iterBuild(self, makeObj: Tokenizer, filtered: dict) -> Iterable[tuple[int, bool]]:
+ """Iterate over buildable documents."""
self._count = True
for i, tHandle in enumerate(self._queue):
self._error = None
@@ -171,116 +215,8 @@ class NWBuildDocument:
yield i, self._doBuild(makeObj, tHandle)
else:
yield i, False
-
- makeObj.closeDocument()
-
- self._error = None
- self._cache = makeObj
-
- try:
- makeObj.saveDocument(path)
- except Exception as exc:
- logException()
- self._error = formatException(exc)
-
return
- def iterBuildHTML(self, path: Path | None, asJson: bool = False) -> Iterable[tuple[int, bool]]:
- """Build an HTML file. If path is None, no file is saved. This
- is used for generating build previews.
- """
- makeObj = ToHtml(self._project)
- filtered = self._setupBuild(makeObj)
-
- self._count = False
- for i, tHandle in enumerate(self._queue):
- self._error = None
- if filtered.get(tHandle, (False, 0))[0]:
- yield i, self._doBuild(makeObj, tHandle)
- else:
- yield i, False
-
- makeObj.appendFootnotes()
-
- if not self._build.getBool("html.preserveTabs"):
- makeObj.replaceTabs()
-
- self._error = None
- self._cache = makeObj
-
- if isinstance(path, Path):
- try:
- makeObj.saveDocument(path, asJson=asJson)
- except Exception as exc:
- logException()
- self._error = formatException(exc)
-
- return
-
- def iterBuildMarkdown(self, path: Path, extendedMd: bool) -> Iterable[tuple[int, bool]]:
- """Build a Markdown file."""
- makeObj = ToMarkdown(self._project)
- filtered = self._setupBuild(makeObj)
-
- makeObj.setExtendedMarkdown(extendedMd)
- if self._build.getBool("format.replaceTabs"):
- makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
-
- self._count = False
- for i, tHandle in enumerate(self._queue):
- self._error = None
- if filtered.get(tHandle, (False, 0))[0]:
- yield i, self._doBuild(makeObj, tHandle)
- else:
- yield i, False
-
- makeObj.appendFootnotes()
-
- self._error = None
- self._cache = makeObj
-
- try:
- makeObj.saveDocument(path)
- except Exception as exc:
- logException()
- self._error = formatException(exc)
-
- return
-
- def iterBuildNWD(self, path: Path | None, asJson: bool = False) -> Iterable[tuple[int, bool]]:
- """Build a novelWriter Markdown file."""
- makeObj = ToMarkdown(self._project)
- filtered = self._setupBuild(makeObj)
-
- makeObj.setKeepMarkdown(True)
-
- self._count = False
- for i, tHandle in enumerate(self._queue):
- self._error = None
- if filtered.get(tHandle, (False, 0))[0]:
- yield i, self._doBuild(makeObj, tHandle, convert=False)
- else:
- yield i, False
-
- if self._build.getBool("format.replaceTabs"):
- makeObj.replaceTabs(nSpaces=4, spaceChar=" ")
-
- self._error = None
- self._cache = makeObj
-
- if isinstance(path, Path):
- try:
- makeObj.saveRawDocument(path, asJson=asJson)
- except Exception as exc:
- logException()
- self._error = formatException(exc)
-
- return
-
- ##
- # Internal Functions
- ##
-
def _setupBuild(self, bldObj: Tokenizer) -> dict:
"""Configure the build object."""
# Get Settings
diff --git a/novelwriter/formats/tohtml.py b/novelwriter/formats/tohtml.py
index 78f359b9..7eb4b33d 100644
--- a/novelwriter/formats/tohtml.py
+++ b/novelwriter/formats/tohtml.py
@@ -290,9 +290,9 @@ class ToHtml(Tokenizer):
return
- def saveDocument(self, path: str | Path, asJson: bool = False) -> None:
+ def saveDocument(self, path: Path) -> None:
"""Save the data to an HTML file."""
- if asJson:
+ if path.suffix.lower() == ".json":
ts = time()
data = {
"meta": {
diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py
index ea003b4c..a2db6cce 100644
--- a/novelwriter/formats/tokenizer.py
+++ b/novelwriter/formats/tokenizer.py
@@ -134,10 +134,10 @@ class Tokenizer(ABC):
self._project = project
# Data Variables
- self._text = "" # The raw text to be tokenized
- self._handle = None # The item handle currently being processed
- self._result = "" # The result of the last document
- self._keepMD = False # Whether to keep the markdown text
+ self._text = "" # The raw text to be tokenized
+ self._handle = None # The item handle currently being processed
+ self._result = "" # The result of the last document
+ self._keepRaw = False # Whether to keep the raw text, used by ToRaw
# Tokens and Meta Data (Per Document)
self._tokens: list[T_Token] = []
@@ -473,11 +473,6 @@ class Tokenizer(ABC):
self._keepBreaks = state
return
- def setKeepMarkdown(self, state: bool) -> None:
- """Keep original markdown during build."""
- self._keepMD = state
- return
-
##
# Class Methods
##
@@ -487,7 +482,7 @@ class Tokenizer(ABC):
raise NotImplementedError
@abstractmethod
- def saveDocument(self, path: str | Path) -> None:
+ def saveDocument(self, path: Path) -> None:
raise NotImplementedError
def addRootHeading(self, tHandle: str) -> None:
@@ -509,7 +504,7 @@ class Tokenizer(ABC):
self._tokens.append((
self.T_TITLE, 1, title, [], textAlign
))
- if self._keepMD:
+ if self._keepRaw:
self._markdown.append(f"#! {title}\n\n")
return
@@ -574,7 +569,7 @@ class Tokenizer(ABC):
tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append("\n")
continue
@@ -632,26 +627,26 @@ class Tokenizer(ABC):
tokens.append((
self.T_SYNOPSIS, nHead, tLine, tFmt, sAlign
))
- if self._doSynopsis and self._keepMD:
+ if self._doSynopsis and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.SHORT:
tLine, tFmt = self._extractFormats(cText)
tokens.append((
self.T_SHORT, nHead, tLine, tFmt, sAlign
))
- if self._doSynopsis and self._keepMD:
+ if self._doSynopsis and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.FOOTNOTE:
tLine, tFmt = self._extractFormats(cText, skip=self.FMT_FNOTE)
self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
else:
tLine, tFmt = self._extractFormats(cText)
tokens.append((
self.T_COMMENT, nHead, tLine, tFmt, sAlign
))
- if self._doComments and self._keepMD:
+ if self._doComments and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("@"):
@@ -668,7 +663,7 @@ class Tokenizer(ABC):
tokens.append((
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
))
- if self._doKeywords and self._keepMD:
+ if self._doKeywords and self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("# ", "#! ")):
@@ -704,7 +699,7 @@ class Tokenizer(ABC):
tokens.append((
tType, nHead, tText, [], tStyle
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("## ", "##! ")):
@@ -739,7 +734,7 @@ class Tokenizer(ABC):
tokens.append((
tType, nHead, tText, [], tStyle
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("### ", "###! ")):
@@ -780,7 +775,7 @@ class Tokenizer(ABC):
tokens.append((
tType, nHead, tText, [], tStyle
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("#### "):
@@ -810,7 +805,7 @@ class Tokenizer(ABC):
tokens.append((
tType, nHead, tText, [], tStyle
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
else:
@@ -858,7 +853,7 @@ class Tokenizer(ABC):
tokens.append((
self.T_TEXT, nHead, tLine, tFmt, sAlign
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append(f"{aLine}\n")
# If we have content, turn off the first page flag
@@ -877,7 +872,7 @@ class Tokenizer(ABC):
tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE
))
- if self._keepMD:
+ if self._keepRaw:
tmpMarkdown.append("\n")
self._markdown.append("".join(tmpMarkdown))
diff --git a/novelwriter/formats/tomarkdown.py b/novelwriter/formats/tomarkdown.py
index a738513c..82526cd5 100644
--- a/novelwriter/formats/tomarkdown.py
+++ b/novelwriter/formats/tomarkdown.py
@@ -81,11 +81,11 @@ class ToMarkdown(Tokenizer):
supports concatenating novelWriter markup files.
"""
- def __init__(self, project: NWProject) -> None:
+ def __init__(self, project: NWProject, extended: bool) -> None:
super().__init__(project)
self._fullMD: list[str] = []
self._usedNotes: dict[str, int] = {}
- self._extended = True
+ self._extended = extended
return
##
@@ -97,15 +97,6 @@ class ToMarkdown(Tokenizer):
"""Return the markdown as a list."""
return self._fullMD
- ##
- # Setters
- ##
-
- def setExtendedMarkdown(self, state: bool) -> None:
- """Set the converter to use Extended Markdown formatting."""
- self._extended = state
- return
-
##
# Class Methods
##
@@ -199,7 +190,7 @@ class ToMarkdown(Tokenizer):
return
- def saveDocument(self, path: str | Path) -> None:
+ def saveDocument(self, path: Path) -> None:
"""Save the data to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile:
outFile.write("".join(self._fullMD))
@@ -210,8 +201,6 @@ class ToMarkdown(Tokenizer):
"""Replace tabs with spaces."""
spaces = spaceChar*nSpaces
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
- if self._keepMD:
- self._markdown = [p.replace("\t", spaces) for p in self._markdown]
return
##
diff --git a/novelwriter/formats/toodt.py b/novelwriter/formats/toodt.py
index 8d3de780..bfb9d80e 100644
--- a/novelwriter/formats/toodt.py
+++ b/novelwriter/formats/toodt.py
@@ -543,7 +543,7 @@ class ToOdt(Tokenizer):
self._xText.insert(0, xFields)
return
- def saveDocument(self, path: str | Path) -> None:
+ def saveDocument(self, path: Path) -> None:
"""Save the data to an .fodt or .odt file."""
if self._isFlat:
with open(path, mode="wb") as fObj:
diff --git a/novelwriter/formats/toqdoc.py b/novelwriter/formats/toqdoc.py
index 43f10b69..07c54e45 100644
--- a/novelwriter/formats/toqdoc.py
+++ b/novelwriter/formats/toqdoc.py
@@ -275,7 +275,7 @@ class ToQTextDocument(Tokenizer):
return
- def saveDocument(self, path: str | Path) -> None:
+ def saveDocument(self, path: Path) -> None:
"""Not implemented."""
return
diff --git a/novelwriter/formats/toraw.py b/novelwriter/formats/toraw.py
new file mode 100644
index 00000000..d1c701b1
--- /dev/null
+++ b/novelwriter/formats/toraw.py
@@ -0,0 +1,86 @@
+"""
+novelWriter – Raw NW Text Format
+================================
+
+File History:
+Created: 2024-10-15 [2.6b1] ToRaw
+
+This file is a part of novelWriter
+Copyright 2018–2024, Veronica Berglyd Olsen
+
+This program is free software: you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program. If not, see .
+"""
+from __future__ import annotations
+
+import json
+import logging
+
+from pathlib import Path
+from time import time
+
+from novelwriter.common import formatTimeStamp
+from novelwriter.core.project import NWProject
+from novelwriter.formats.tokenizer import Tokenizer
+
+logger = logging.getLogger(__name__)
+
+
+class ToRaw(Tokenizer):
+ """Core: Raw novelWriter Text Writer
+
+ A class that will collect the minimally altered original source text
+ and write it to either a text or JSON file.
+ """
+
+ def __init__(self, project: NWProject) -> None:
+ super().__init__(project)
+ self._keepRaw = True
+ return
+
+ def doConvert(self) -> None:
+ """No conversion to perform."""
+ return
+
+ def saveDocument(self, path: Path) -> None:
+ """Save the raw text to a plain text file."""
+ if path.suffix.lower() == ".json":
+ ts = time()
+ data = {
+ "meta": {
+ "projectName": self._project.data.name,
+ "novelAuthor": self._project.data.author,
+ "buildTime": int(ts),
+ "buildTimeStr": formatTimeStamp(ts),
+ },
+ "text": {
+ "nwd": [page.rstrip("\n").split("\n") for page in self._markdown],
+ }
+ }
+ with open(path, mode="w", encoding="utf-8") as fObj:
+ json.dump(data, fObj, indent=2)
+
+ else:
+ with open(path, mode="w", encoding="utf-8") as outFile:
+ for nwdPage in self._markdown:
+ outFile.write(nwdPage)
+
+ logger.info("Wrote file: %s", path)
+
+ return
+
+ def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
+ """Replace tabs with spaces."""
+ spaces = spaceChar*nSpaces
+ self._markdown = [p.replace("\t", spaces) for p in self._markdown]
+ return
diff --git a/novelwriter/tools/manusbuild.py b/novelwriter/tools/manusbuild.py
index cf18822b..e37bfca5 100644
--- a/novelwriter/tools/manusbuild.py
+++ b/novelwriter/tools/manusbuild.py
@@ -333,7 +333,7 @@ class GuiManuscriptBuild(NDialog):
docBuild.queueAll()
self.buildProgress.setMaximum(len(docBuild))
- for i, _ in docBuild.iterBuild(buildPath, bFormat):
+ for i, _ in docBuild.iterBuildDocument(buildPath, bFormat):
self.buildProgress.setValue(i+1)
self._build.setLastBuildPath(bPath)
diff --git a/tests/reference/mBuildDocBuild_NWD_Lorem_Ipsum.json b/tests/reference/mBuildDocBuild_NWD_Lorem_Ipsum.json
index 333a90e1..630d9592 100644
--- a/tests/reference/mBuildDocBuild_NWD_Lorem_Ipsum.json
+++ b/tests/reference/mBuildDocBuild_NWD_Lorem_Ipsum.json
@@ -2,8 +2,8 @@
"meta": {
"projectName": "Lorem Ipsum",
"novelAuthor": "lipsum.com",
- "buildTime": 1714229171,
- "buildTimeStr": "2024-04-27 16:46:11"
+ "buildTime": 1729029144,
+ "buildTimeStr": "2024-10-15 23:52:24"
},
"text": {
"nwd": [
diff --git a/tests/test_core/test_core_docbuild.py b/tests/test_core/test_core_docbuild.py
index 391cc2b6..23feea8b 100644
--- a/tests/test_core/test_core_docbuild.py
+++ b/tests/test_core/test_core_docbuild.py
@@ -34,6 +34,7 @@ from novelwriter.enum import nwBuildFmt
from novelwriter.formats.tohtml import ToHtml
from novelwriter.formats.tomarkdown import ToMarkdown
from novelwriter.formats.toodt import ToOdt
+from novelwriter.formats.toraw import ToRaw
from tests.mocked import causeException, causeOSError
from tests.tools import ODT_IGNORE, C, buildTestProject, cmpFiles
@@ -101,7 +102,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
count = 0
error = []
- for _, success in docBuild.iterBuildOpenDocument(docFile, True):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.FODT):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -119,7 +120,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
count = 0
error = []
- for _, success in docBuild.iterBuildOpenDocument(docFile, False):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.ODT):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -136,7 +137,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
mp.setattr("builtins.open", causeOSError)
docFile = fncPath / "Lorem Ipsum Err.fodt"
- for _ in docBuild.iterBuildOpenDocument(docFile, True):
+ for _ in docBuild.iterBuildDocument(docFile, nwBuildFmt.FODT):
pass
assert docBuild.error == "OSError: Mock OSError"
@@ -152,7 +153,7 @@ def testCoreDocBuild_OpenDocument(monkeypatch, mockGUI, prjLipsum, fncPath, tstP
count = 0
error = []
docFile = fncPath / "Lorem Ipsum Err.fodt"
- for _, success in docBuild.iterBuildOpenDocument(docFile, True):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.FODT):
count += 1 if success else 0
if not success and docBuild.error:
error.append(docBuild.error)
@@ -203,7 +204,7 @@ def testCoreDocBuild_HTML(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
count = 0
error = []
- for _, success in docBuild.iterBuildHTML(docFile):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.HTML):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -223,7 +224,7 @@ def testCoreDocBuild_HTML(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
count = 0
error = []
- for _, success in docBuild.iterBuildHTML(docFile, asJson=True):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.J_HTML):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -241,7 +242,7 @@ def testCoreDocBuild_HTML(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
mp.setattr("builtins.open", causeOSError)
docFile = fncPath / "Lorem Ipsum Err.htm"
- for _ in docBuild.iterBuildHTML(docFile):
+ for _ in docBuild.iterBuildDocument(docFile, nwBuildFmt.HTML):
pass
assert docBuild.error == "OSError: Mock OSError"
@@ -271,7 +272,7 @@ def testCoreDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths
count = 0
error = []
- for _, success in docBuild.iterBuildMarkdown(docFile, False):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.STD_MD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -291,7 +292,7 @@ def testCoreDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths
count = 0
error = []
- for _, success in docBuild.iterBuildMarkdown(docFile, True):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.EXT_MD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -309,7 +310,7 @@ def testCoreDocBuild_Markdown(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths
mp.setattr("builtins.open", causeOSError)
docFile = fncPath / "Lorem Ipsum Err.md"
- for _ in docBuild.iterBuildMarkdown(docFile, False):
+ for _ in docBuild.iterBuildDocument(docFile, nwBuildFmt.STD_MD):
pass
assert docBuild.error == "OSError: Mock OSError"
@@ -339,7 +340,7 @@ def testCoreDocBuild_NWD(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
count = 0
error = []
- for _, success in docBuild.iterBuildNWD(docFile, asJson=False):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.NWD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -359,7 +360,7 @@ def testCoreDocBuild_NWD(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
count = 0
error = []
- for _, success in docBuild.iterBuildNWD(docFile, asJson=True):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.J_NWD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -377,7 +378,7 @@ def testCoreDocBuild_NWD(monkeypatch, mockGUI, prjLipsum, fncPath, tstPaths):
mp.setattr("builtins.open", causeOSError)
docFile = fncPath / "Lorem Ipsum Err.md"
- for _ in docBuild.iterBuildNWD(docFile):
+ for _ in docBuild.iterBuildDocument(docFile, nwBuildFmt.NWD):
pass
assert docBuild.error == "OSError: Mock OSError"
@@ -401,7 +402,7 @@ def testCoreDocBuild_Custom(mockGUI, fncPath: Path):
count = 0
error = []
docFile = fncPath / "Minimal.txt"
- for _, success in docBuild.iterBuildNWD(docFile, asJson=False):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.NWD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -431,7 +432,7 @@ def testCoreDocBuild_Custom(mockGUI, fncPath: Path):
count = 0
error = []
docFile = fncPath / "Minimal.txt"
- for _, success in docBuild.iterBuildNWD(docFile, asJson=False):
+ for _, success in docBuild.iterBuildDocument(docFile, nwBuildFmt.NWD):
count += 1 if success else 0
if docBuild.error:
error.append(docBuild.error)
@@ -473,7 +474,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# ODT Format
docFile = fncPath / "Minimal.odt"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.ODT)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.ODT)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -483,7 +484,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# FODT Format
docFile = fncPath / "Minimal.fodt"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.FODT)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.FODT)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -493,7 +494,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# HTML Format
docFile = fncPath / "Minimal.html"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.HTML)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.HTML)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -503,7 +504,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# JSON HTML Format
docFile = fncPath / "Minimal.json"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.J_HTML)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.J_HTML)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -515,7 +516,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# Standard Markdown Format
docFile = fncPath / "Minimal.md"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.STD_MD)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.STD_MD)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -536,7 +537,7 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# Extended Markdown Format
docFile = fncPath / "Minimal.md"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.EXT_MD)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.EXT_MD)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
@@ -557,11 +558,11 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# NWD Format
docFile = fncPath / "Minimal.txt"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.NWD)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.NWD)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
- assert isinstance(docBuild.lastBuild, ToMarkdown)
+ assert isinstance(docBuild.lastBuild, ToRaw)
assert docFile.read_text(encoding="utf-8") == (
"#! New Novel\n\n"
"By Jane Doe\n\n"
@@ -578,11 +579,11 @@ def testCoreDocBuild_IterBuild(mockGUI, fncPath: Path, mockRnd):
# JSON NWD Format
docFile = fncPath / "Minimal.json"
- assert list(docBuild.iterBuild(docFile, nwBuildFmt.J_NWD)) == [
+ assert list(docBuild.iterBuildDocument(docFile, nwBuildFmt.J_NWD)) == [
(0, True), (1, True), (2, False), (3, True), (4, True),
(5, True), (6, True), (7, True), (8, True), (9, False),
]
- assert isinstance(docBuild.lastBuild, ToMarkdown)
+ assert isinstance(docBuild.lastBuild, ToRaw)
data = json.loads(docFile.read_text(encoding="utf-8"))
assert "meta" in data
assert "text" in data
diff --git a/tests/test_formats/test_fmt_tohtml.py b/tests/test_formats/test_fmt_tohtml.py
index 49e7bf63..c1467566 100644
--- a/tests/test_formats/test_fmt_tohtml.py
+++ b/tests/test_formats/test_fmt_tohtml.py
@@ -644,12 +644,12 @@ def testFmtToHtml_Save(mockGUI, fncPath):
)
saveFile = fncPath / "outFile.htm"
- html.saveDocument(saveFile, asJson=False)
+ html.saveDocument(saveFile)
assert saveFile.read_text(encoding="utf-8") == htmlDoc
# JSON + HTML
saveFile = fncPath / "outFile.json"
- html.saveDocument(saveFile, asJson=True)
+ html.saveDocument(saveFile)
data = json.loads(saveFile.read_text(encoding="utf-8"))
assert data["meta"]["projectName"] == ""
assert data["meta"]["novelAuthor"] == ""
@@ -664,7 +664,6 @@ def testFmtToHtml_Methods(mockGUI):
"""Test all the other methods of the ToHtml class."""
project = NWProject()
html = ToHtml(project)
- html.setKeepMarkdown(True)
# Auto-Replace, keep Unicode
docText = "Text with & short–dash, long—dash …\n"
diff --git a/tests/test_formats/test_fmt_tokenizer.py b/tests/test_formats/test_fmt_tokenizer.py
index 33531f02..8cbcdaf0 100644
--- a/tests/test_formats/test_fmt_tokenizer.py
+++ b/tests/test_formats/test_fmt_tokenizer.py
@@ -31,6 +31,7 @@ from novelwriter.constants import nwHeadFmt
from novelwriter.core.project import NWProject
from novelwriter.formats.tokenizer import HeadingFormatter, Tokenizer, stripEscape
from novelwriter.formats.tomarkdown import ToMarkdown
+from novelwriter.formats.toraw import ToRaw
from tests.tools import C, buildTestProject, readFile
@@ -43,6 +44,19 @@ class BareTokenizer(Tokenizer):
super().saveDocument(path) # type: ignore (deliberate check)
+@pytest.mark.core
+def testFmtToken_Abstracts(mockGUI, tstPaths):
+ """Test all the abstract methods of the Tokenizer class."""
+ project = NWProject()
+ tokens = BareTokenizer(project)
+
+ with pytest.raises(NotImplementedError):
+ tokens.doConvert()
+
+ with pytest.raises(NotImplementedError):
+ tokens.saveDocument(tstPaths)
+
+
@pytest.mark.core
def testFmtToken_Setters(mockGUI):
"""Test all the setters for the Tokenizer class."""
@@ -164,8 +178,7 @@ def testFmtToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
project.data.setLanguage("en")
project._loadProjectLocalisation()
- tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
+ tokens = ToRaw(project)
# Set some content to work with
docText = (
@@ -235,13 +248,6 @@ def testFmtToken_TextOps(monkeypatch, mockGUI, mockRnd, fncPath):
]
}
- # Check abstract methods
- with pytest.raises(NotImplementedError):
- tokens.doConvert()
-
- with pytest.raises(NotImplementedError):
- tokens.saveDocument(fncPath)
-
@pytest.mark.core
def testFmtToken_StripEscape():
@@ -256,8 +262,7 @@ def testFmtToken_StripEscape():
def testFmtToken_HeaderFormat(mockGUI):
"""Test the tokenization of header formats in the Tokenizer class."""
project = NWProject()
- tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
+ tokens = ToRaw(project)
# Title
# =====
@@ -692,8 +697,7 @@ def testFmtToken_HeaderStyle(mockGUI):
def testFmtToken_MetaFormat(mockGUI):
"""Test the tokenization of meta formats in the Tokenizer class."""
project = NWProject()
- tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
+ tokens = ToRaw(project)
# Comment
tokens._text = "% A comment\n"
@@ -780,8 +784,7 @@ def testFmtToken_MetaFormat(mockGUI):
def testFmtToken_MarginFormat(mockGUI):
"""Test the tokenization of margin formats in the Tokenizer class."""
project = NWProject()
- tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
+ tokens = ToRaw(project)
# Alignment and Indentation
dblIndent = Tokenizer.A_IND_L | Tokenizer.A_IND_R
@@ -824,7 +827,6 @@ def testFmtToken_ExtractFormats(mockGUI):
"""Test the extraction of formats in the Tokenizer class."""
project = NWProject()
tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
# Markdown
# ========
@@ -931,7 +933,6 @@ def testFmtToken_Paragraphs(mockGUI):
"""Test the splitting of paragraphs."""
project = NWProject()
tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
# Collapse empty lines
tokens._text = "First paragraph\n\n\nSecond paragraph\n\n\n"
@@ -1003,8 +1004,7 @@ def testFmtToken_Paragraphs(mockGUI):
def testFmtToken_TextFormat(mockGUI):
"""Test the tokenization of text formats in the Tokenizer class."""
project = NWProject()
- tokens = BareTokenizer(project)
- tokens.setKeepMarkdown(True)
+ tokens = ToRaw(project)
# Text
tokens._text = "Some plain text\non two lines\n\n\n"
@@ -1887,7 +1887,7 @@ def testFmtToken_SceneSeparators(mockGUI):
project = NWProject()
project.data.setLanguage("en")
project._loadProjectLocalisation()
- md = ToMarkdown(project)
+ md = ToMarkdown(project, False)
md._isNovel = True
# Separator Handling, Titles
@@ -2002,8 +2002,7 @@ def testFmtToken_SceneSeparators(mockGUI):
# Separators with Scenes Only
# ===========================
# Requires a fresh builder class
- md = ToMarkdown(project)
- md.setExtendedMarkdown(True)
+ md = ToMarkdown(project, True)
md._isNovel = True
md._text = (
@@ -2038,7 +2037,7 @@ def testFmtToken_HeaderVisibility(mockGUI):
project = NWProject()
project.data.setLanguage("en")
project._loadProjectLocalisation()
- md = ToMarkdown(project)
+ md = ToMarkdown(project, False)
md._text = (
"#! Novel\n\n"
@@ -2150,7 +2149,7 @@ def testFmtToken_CounterHandling(mockGUI):
project = NWProject()
project.data.setLanguage("en")
project._loadProjectLocalisation()
- md = ToMarkdown(project)
+ md = ToMarkdown(project, False)
md._isNovel = True
# Counter Handling, Novel Titles
diff --git a/tests/test_formats/test_fmt_tomarkdown.py b/tests/test_formats/test_fmt_tomarkdown.py
index 5f446699..177a2b5b 100644
--- a/tests/test_formats/test_fmt_tomarkdown.py
+++ b/tests/test_formats/test_fmt_tomarkdown.py
@@ -30,7 +30,7 @@ from novelwriter.formats.tomarkdown import ToMarkdown
def testFmtToMarkdown_ConvertHeaders(mockGUI):
"""Test header formats in the ToMarkdown class."""
project = NWProject()
- toMD = ToMarkdown(project)
+ toMD = ToMarkdown(project, False)
toMD._isNovel = True
toMD._isFirst = True
@@ -76,13 +76,13 @@ def testFmtToMarkdown_ConvertHeaders(mockGUI):
def testFmtToMarkdown_ConvertParagraphs(mockGUI):
"""Test paragraph formats in the ToMarkdown class."""
project = NWProject()
- toMD = ToMarkdown(project)
+ toMD = ToMarkdown(project, False)
toMD._isNovel = True
toMD._isFirst = True
# Text for Extended Markdown
- toMD.setExtendedMarkdown(True)
+ toMD._extended = True
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
toMD.tokenizeText()
toMD.doConvert()
@@ -91,7 +91,7 @@ def testFmtToMarkdown_ConvertParagraphs(mockGUI):
)
# Text for Standard Markdown
- toMD.setExtendedMarkdown(False)
+ toMD._extended = False
toMD._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
toMD.tokenizeText()
toMD.doConvert()
@@ -100,7 +100,7 @@ def testFmtToMarkdown_ConvertParagraphs(mockGUI):
)
# Shortcodes for Extended Markdown
- toMD.setExtendedMarkdown(True)
+ toMD._extended = True
toMD._text = (
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
@@ -113,7 +113,7 @@ def testFmtToMarkdown_ConvertParagraphs(mockGUI):
)
# Shortcodes for Standard Markdown
- toMD.setExtendedMarkdown(False)
+ toMD._extended = False
toMD._text = (
"Some [b]bold[/b], [i]italic[/i], [s]strike[/s], [u]underline[/u], [m]mark[/m], "
"super[sup]script[/sup], sub[sub]script[/sub] here\n"
@@ -212,10 +212,8 @@ def testFmtToMarkdown_ConvertParagraphs(mockGUI):
def testFmtToMarkdown_ConvertDirect(mockGUI):
"""Test the converter directly using the ToMarkdown class."""
project = NWProject()
- toMD = ToMarkdown(project)
-
+ toMD = ToMarkdown(project, False)
toMD._isNovel = True
- toMD.setExtendedMarkdown(False)
# Special Titles
# ==============
@@ -249,8 +247,7 @@ def testFmtToMarkdown_ConvertDirect(mockGUI):
def testFmtToMarkdown_Save(mockGUI, fncPath):
"""Test the save method of the ToMarkdown class."""
project = NWProject()
- toMD = ToMarkdown(project)
- toMD.setKeepMarkdown(True)
+ toMD = ToMarkdown(project, False)
toMD._isNovel = True
# Build Project
@@ -287,7 +284,7 @@ def testFmtToMarkdown_Save(mockGUI, fncPath):
toMD.replaceTabs(nSpaces=4, spaceChar=" ")
resText[6] = "#### A Section\n\n More text in scene two.\n\n"
- assert toMD.allMarkdown == resText
+ assert toMD.fullMD == resText
# Check File
# ==========
@@ -301,7 +298,7 @@ def testFmtToMarkdown_Save(mockGUI, fncPath):
def testFmtToMarkdown_Format(mockGUI):
"""Test all the formatters for the ToMarkdown class."""
project = NWProject()
- toMD = ToMarkdown(project)
+ toMD = ToMarkdown(project, False)
assert toMD._formatKeywords("", toMD.A_NONE) == ""
assert toMD._formatKeywords("tag: Jane", toMD.A_NONE) == "**Tag:** Jane\n\n"