diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py index 6a3e823e..69b9591f 100644 --- a/novelwriter/__init__.py +++ b/novelwriter/__init__.py @@ -110,7 +110,7 @@ CONFIG = Config() def main(sysArgs=None): - """Parse command line, set up logging, and launches main GUI. + """Parse command line, set up logging, and launch main GUI. """ if sysArgs is None: sysArgs = sys.argv[1:] @@ -130,8 +130,8 @@ def main(sysArgs=None): ] helpMsg = ( - "novelWriter {version} ({date})\n" - "{copyright}\n" + f"novelWriter {__version__} ({__date__})\n" + f"{__copyright__}\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" @@ -147,10 +147,6 @@ def main(sysArgs=None): " --style= Sets Qt5 style flag. Defaults to 'Fusion'.\n" " --config= Alternative config file.\n" " --data= Alternative user data path.\n" - ).format( - version=__version__, - copyright=__copyright__, - date=__date__, ) # Defaults @@ -165,9 +161,9 @@ def main(sysArgs=None): # Parse Options try: inOpts, inRemain = getopt.getopt(sysArgs, shortOpt, longOpt) - except getopt.GetoptError as E: + except getopt.GetoptError as exc: print(helpMsg) - print("ERROR: %s" % str(E)) + print(f"ERROR: {str(exc)}") sys.exit(2) if len(inRemain) > 0: @@ -268,7 +264,7 @@ def main(sysArgs=None): elif CONFIG.osWindows: try: import ctypes - appID = "io.novelwriter.%s" % __version__ + appID = f"io.novelwriter.{__version__}" ctypes.windll.shell32.SetCurrentProcessExplicitAppUserModelID(appID) except Exception: logger.error("Failed to set application name") @@ -281,7 +277,7 @@ def main(sysArgs=None): return nwGUI else: - nwApp = QApplication([CONFIG.appName, ("-style=%s" % qtStyle)]) + nwApp = QApplication([CONFIG.appName, (f"-style={qtStyle}")]) nwApp.setApplicationName(CONFIG.appName) nwApp.setApplicationVersion(__version__) nwApp.setWindowIcon(QIcon(CONFIG.appIcon)) diff --git a/novelwriter/common.py b/novelwriter/common.py index 37e9fade..7fb69020 100644 --- a/novelwriter/common.py +++ b/novelwriter/common.py @@ -31,8 +31,8 @@ import logging from datetime import datetime from configparser import ConfigParser -from PyQt5.QtWidgets import qApp from PyQt5.QtCore import QCoreApplication +from PyQt5.QtWidgets import qApp from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout from novelwriter.error import logException diff --git a/novelwriter/core/document.py b/novelwriter/core/document.py index fe84a133..ba75b998 100644 --- a/novelwriter/core/document.py +++ b/novelwriter/core/document.py @@ -56,15 +56,21 @@ class NWDoc(): return + def __repr__(self): + return f"" + + def __bool__(self): + return self._docHandle is not None and bool(self._theItem) + ## # Class Methods ## def readDocument(self, isOrphan=False): - """Read a document from set handle, capturing potential file - system errors and parse meta data. If the document doesn't exist - on disk, return an empty string. If something went wrong, return - None. + """Read the document specified by the handle set in the + contructor, capturing potential file system errors and parse + meta data. If the document doesn't exist on disk, return an + empty string. If something went wrong, return None. """ self._docError = "" if self._docHandle is None: @@ -88,7 +94,6 @@ class NWDoc(): if os.path.isfile(docPath): try: with open(docPath, mode="r", encoding="utf-8") as inFile: - # Check the first <= 10 lines for metadata for i in range(10): inLine = inFile.readline() @@ -108,14 +113,15 @@ class NWDoc(): else: # The document file does not exist, so we assume it's a new # document and initialise an empty text string. - logger.debug("The requested document does not exist.") + logger.debug("The requested document does not exist") return "" return theText def writeDocument(self, docText, forceWrite=False): - """Write the document. The file is saved via a temp file in case - of save failure. Returns True if successful, False if not. + """Write the document specified by the handle attribute. Handle + any IO errors in the process Returns True if successful, False + if not. """ self._docError = "" if self._docHandle is None: @@ -156,9 +162,7 @@ class NWDoc(): # If we're here, the file was successfully saved, so we can # replace the temp file with the actual file - if os.path.isfile(docPath): - os.unlink(docPath) - os.rename(docTemp, docPath) + os.replace(docTemp, docPath) self._prevHash = sha256sum(docPath) self._currHash = self._prevHash @@ -174,11 +178,10 @@ class NWDoc(): logger.error("No document handle set") return False - docFile = self._docHandle+".nwd" - - chkList = [] - chkList.append(os.path.join(self.theProject.projContent, docFile)) - chkList.append(os.path.join(self.theProject.projContent, docFile+"~")) + chkList = [ + os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd"), + os.path.join(self.theProject.projContent, f"{self._docHandle}.nwd~"), + ] for chkFile in chkList: if os.path.isfile(chkFile): @@ -196,18 +199,18 @@ class NWDoc(): ## def getFileLocation(self): - """Return the file location of the current file. + """Return the file location of the current document. """ return self._fileLoc def getCurrentItem(self): - """Return a pointer to the currently open item. + """Return a pointer to the currently open NWItem. """ return self._theItem def getMeta(self): - """Parses the document meta tag and returns the path and name as - a list and a string. + """Parse the document meta tag and return the name, parent, + class and layout meta values. """ theName = self._docMeta.get("name", "") theParent = self._docMeta.get("parent", None) diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index b4d94163..b0faae08 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -27,7 +27,6 @@ along with this program. If not, see . import os import json import logging -import novelwriter from time import time @@ -49,10 +48,10 @@ class NWIndex(): def __init__(self, theProject): + self.theProject = theProject + # Internal - self.mainConf = novelwriter.CONFIG - self.theProject = theProject - self.indexBroken = False + self._indexBroken = False # Indices self._tagIndex = {} @@ -67,6 +66,10 @@ class NWIndex(): return + @property + def indexBroken(self): + return self._indexBroken + ## # Public Methods ## @@ -88,11 +91,7 @@ class NWIndex(): """ logger.debug("Removing item '%s' from the index", tHandle) - delTags = [] - for tTag in self._tagIndex: - if self._tagIndex[tTag][1] == tHandle: - delTags.append(tTag) - + delTags = list(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex)) for tTag in delTags: self._tagIndex.pop(tTag, None) @@ -157,7 +156,7 @@ class NWIndex(): except Exception: logger.error("Failed to load index file") logException() - self.indexBroken = True + self._indexBroken = True return False self._tagIndex = theData.get("tagIndex", {}) @@ -214,17 +213,17 @@ class NWIndex(): self._checkRefIndex() self._checkFileIndex() self._checkFileMeta() - self.indexBroken = False + self._indexBroken = False except Exception: logger.error("Error while checking index") logException() - self.indexBroken = True + self._indexBroken = True logger.verbose("Index check took %.3f ms", (time() - tStart)*1000) logger.debug("Index check complete") - if self.indexBroken: + if self._indexBroken: self.clearIndex() return @@ -237,7 +236,8 @@ class NWIndex(): """Scan a piece of text associated with a handle. This will update the indices accordingly. This function takes the handle and text as separate inputs as we want to primarily scan the - files before we save them, unless we're rebuilding the index. + files before we save them in which case we already have the + text. """ theItem = self.theProject.projTree[tHandle] theRoot = self.theProject.projTree.getRootItem(tHandle) @@ -259,7 +259,7 @@ class NWIndex(): cC, wC, pC = countWords(theText) self._fileMeta[tHandle] = ["H0", cC, wC, pC] - # If the file is archived or trashed, we don't index the file itself + # If the file is archived or in trash, we don't index the content if self.theProject.projTree.isTrashRoot(theItem.itemParent): logger.debug("Not indexing trash item '%s'", tHandle) return False @@ -276,16 +276,12 @@ class NWIndex(): self._refIndex.pop(tHandle, None) self._fileIndex[tHandle] = {} - # Also clear references to file in tag index - clearTags = [] - for aTag in self._tagIndex: - if self._tagIndex[aTag][1] == tHandle: - clearTags.append(aTag) + # Also clear references to the file in the tags index + clearTags = list(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex)) for aTag in clearTags: self._tagIndex.pop(aTag) # Scan the text content - nLine = 0 nTitle = 0 theLines = theText.splitlines() for nLine, aLine in enumerate(theLines, start=1): @@ -355,7 +351,7 @@ class NWIndex(): hText = aLine[5:].strip() elif aLine.startswith("#! "): hDepth = "H1" - hText = aLine[2:].strip() + hText = aLine[3:].strip() elif aLine.startswith("##! "): hDepth = "H2" hText = aLine[4:].strip() @@ -374,6 +370,8 @@ class NWIndex(): } if self._fileMeta[tHandle][0] == "H0": + # Since this initialises to H0, this ensures that only the + # first header level is recorded in the file meta index self._fileMeta[tHandle][0] = hDepth return True @@ -542,8 +540,7 @@ class NWIndex(): hCount = [0, 0, 0, 0, 0] for tHandle in self._listNovelHandles(skipExcluded): for sTitle in self._fileIndex[tHandle]: - theData = self._fileIndex[tHandle][sTitle] - iLevel = H_LEVEL.get(theData["level"], 0) + iLevel = H_LEVEL.get(self._fileIndex[tHandle][sTitle]["level"], 0) hCount[iLevel] += 1 return hCount @@ -551,45 +548,29 @@ class NWIndex(): def getHandleWordCounts(self, tHandle): """Get all header word counts for a specific handle. """ - theCounts = [] - hRecord = self._fileIndex.get(tHandle, None) - if hRecord is None: - return theCounts - - for sTitle, sData in hRecord.items(): - theCounts.append((f"{tHandle}:{sTitle}", sData["wCount"])) - - return theCounts + hRecord = self._fileIndex.get(tHandle, {}) + return [(f"{tHandle}:{sTitle}", sData["wCount"]) for sTitle, sData in hRecord.items()] def getHandleHeaders(self, tHandle): """Get all headers for a specific handle. """ - theHeaders = [] - hRecord = self._fileIndex.get(tHandle, None) - if hRecord is None: - return theHeaders - - for sTitle, sData in hRecord.items(): - theHeaders.append((sTitle, sData["level"], sData["title"])) - - return theHeaders + hRecord = self._fileIndex.get(tHandle, {}) + return [(sTitle, sData["level"], sData["title"]) for sTitle, sData in hRecord.items()] def getHandleHeaderLevel(self, tHandle): """Get the header level of the first header of a handle. """ - if tHandle in self._fileMeta: - return self._fileMeta[tHandle][0] - return "H0" + return self._fileMeta.get(tHandle, ["H0"])[0] def getTableOfContents(self, maxDepth, skipExcluded=True): - """Generate a table of contents up to a maxiumum depth. + """Generate a table of contents up to a maximum depth. """ tOrder = [] tData = {} pKey = None for tHandle in self._listNovelHandles(skipExcluded): for sTitle in sorted(self._fileIndex[tHandle]): - tKey = "%s:%s" % (tHandle, sTitle) + tKey = f"{tHandle}:{sTitle}" theData = self._fileIndex[tHandle][sTitle] iLevel = H_LEVEL.get(theData["level"], 0) if iLevel > maxDepth: @@ -605,19 +586,17 @@ class NWIndex(): "words": theData["wCount"], } - theToC = [] - for tKey in tOrder: - theToC.append(( - tKey, - tData[tKey]["level"], - tData[tKey]["title"], - tData[tKey]["words"], - )) + theToC = [( + tKey, + tData[tKey]["level"], + tData[tKey]["title"], + tData[tKey]["words"] + ) for tKey in tOrder] return theToC def getCounts(self, tHandle, sTitle=None): - """Returns the counts for a file, or a section of a file + """Return the counts for a file, or a section of a file, starting at title sTitle if it is provided. """ cC = 0 @@ -640,12 +619,9 @@ class NWIndex(): def getReferences(self, tHandle, sTitle=None): """Extract all references made in a file, and optionally title - section. sTitle must be a string. + section. """ - theRefs = {} - for tKey in nwKeyWords.KEY_CLASS: - theRefs[tKey] = [] - + theRefs = {x: [] for x in nwKeyWords.KEY_CLASS} if tHandle not in self._refIndex: return theRefs @@ -669,15 +645,11 @@ class NWIndex(): """Build a list of files referring back to our file, specified by tHandle. """ - theRefs = {} if tHandle is None: - return theRefs - - theTags = set() - for tTag in self._tagIndex: - if tHandle == self._tagIndex[tTag][1]: - theTags.add(tTag) + return {} + theRefs = {} + theTags = set(filter(lambda x: self._tagIndex[x][1] == tHandle, self._tagIndex)) if theTags: for tHandle in self._refIndex: for sTitle in self._refIndex[tHandle]: @@ -690,10 +662,9 @@ class NWIndex(): def getTagSource(self, theTag): """Return the source location of a given tag. """ - if theTag in self._tagIndex: - theRef = self._tagIndex[theTag] - if len(theRef) == 4: - return theRef[1], theRef[0], theRef[3] + theRef = self._tagIndex.get(theTag, []) + if len(theRef) == 4: + return theRef[1], theRef[0], theRef[3] return None, 0, "T000000" ## @@ -859,9 +830,9 @@ def countWords(theText): return charCount, wordCount, paraCount # We need to treat dashes as word separators for counting words. - # The check+replace apprach is much faster that direct replace for + # The check+replace approach is much faster than direct replace for # large texts, and a bit slower for small texts, but in the latter - # case it doesn't matter. + # case it doesn't really matter. if nwUnicode.U_ENDASH in theText: theText = theText.replace(nwUnicode.U_ENDASH, " ") if nwUnicode.U_EMDASH in theText: diff --git a/novelwriter/core/item.py b/novelwriter/core/item.py index 985a9e8c..b285f69d 100644 --- a/novelwriter/core/item.py +++ b/novelwriter/core/item.py @@ -67,7 +67,7 @@ class NWItem(): ## def packXML(self, xParent): - """Packs all the data in the class instance into an XML object. + """Pack all the data in the class instance into an XML object. """ xPack = etree.SubElement(xParent, "item", attrib={ "handle": str(self.itemHandle), @@ -91,7 +91,7 @@ class NWItem(): return def unpackXML(self, xItem): - """Sets the values from an XML entry of type 'item'. + """Set the values from an XML entry of type 'item'. """ if xItem.tag != "item": logger.error("XML entry is not an NWItem") @@ -133,7 +133,7 @@ class NWItem(): else: # Sliently skip as we may otherwise cause orphaned # items if an otherwise valid file is opened by a - # version of novelWriter that doesn't know the tag. + # version of novelWriter that doesn't know the tag logger.error("Unknown tag '%s'", xValue.tag) # Guarantees that is parsed after @@ -143,7 +143,7 @@ class NWItem(): @staticmethod def _subPack(xParent, name, attrib=None, text=None, none=True): - """Packs the values into an xml element. + """Pack the values into an XML element. """ if not none and (text is None or text == "None"): return None @@ -204,7 +204,7 @@ class NWItem(): return def setParent(self, theParent): - """Set the parent handle, and ensure that it is valid. + """Set the parent handle, and ensure it is valid. """ if theParent is None: self.itemParent = None @@ -216,14 +216,15 @@ class NWItem(): def setOrder(self, theOrder): """Set the item order, and ensure that it is valid. This value - is purely a meta value, not actually used by novelWriter. + is purely a meta value, and not actually used by novelWriter at + the moment. """ self.itemOrder = checkInt(theOrder, 0) return def setType(self, theType): """Set the item type from either a proper nwItemType, or set it - from a string representing a nwItemType. + from a string representing an nwItemType. """ if isinstance(theType, nwItemType): self.itemType = theType @@ -236,7 +237,7 @@ class NWItem(): def setClass(self, theClass): """Set the item class from either a proper nwItemClass, or set - it from a string representing a nwItemClass. + it from a string representing an nwItemClass. """ if isinstance(theClass, nwItemClass): self.itemClass = theClass @@ -249,7 +250,7 @@ class NWItem(): def setLayout(self, theLayout): """Set the item layout from either a proper nwItemLayout, or set - it from a string representing a nwItemLayout. + it from a string representing an nwItemLayout. """ if isinstance(theLayout, nwItemLayout): self.itemLayout = theLayout @@ -273,7 +274,7 @@ class NWItem(): return def setExpanded(self, expState): - """Save the expanded status of an item in the project tree. + """Set the expanded status of an item in the project tree. """ if isinstance(expState, str): self.isExpanded = (expState == str(True)) @@ -282,7 +283,7 @@ class NWItem(): return def setExported(self, expState): - """Save the export flag. + """Set the export flag. """ if isinstance(expState, str): self.isExported = (expState == str(True)) @@ -297,29 +298,29 @@ class NWItem(): def setCharCount(self, theCount): """Set the character count, and ensure that it is an integer. """ - self.charCount = checkInt(theCount, 0) + self.charCount = max(0, checkInt(theCount, 0)) return def setWordCount(self, theCount): """Set the word count, and ensure that it is an integer. """ - self.wordCount = checkInt(theCount, 0) + self.wordCount = max(0, checkInt(theCount, 0)) return def setParaCount(self, theCount): """Set the paragraph count, and ensure that it is an integer. """ - self.paraCount = checkInt(theCount, 0) + self.paraCount = max(0, checkInt(theCount, 0)) return def setCursorPos(self, thePosition): """Set the cursor position, and ensure that it is an integer. """ - self.cursorPos = checkInt(thePosition, 0) + self.cursorPos = max(0, checkInt(thePosition, 0)) return def saveInitialCount(self): - """Set the initial word count. + """Save the initial word count. """ self.initCount = self.wordCount return diff --git a/novelwriter/core/options.py b/novelwriter/core/options.py index 2cb8cde0..926e9aa1 100644 --- a/novelwriter/core/options.py +++ b/novelwriter/core/options.py @@ -123,7 +123,7 @@ class OptionState(): ## def setValue(self, group, name, value): - """Saves a value, with a given group and name. + """Save a value, with a given group and name. """ if group not in VALID_MAP: logger.error("Unknown option group '%s'", group) diff --git a/novelwriter/core/project.py b/novelwriter/core/project.py index c2481aab..bc945990 100644 --- a/novelwriter/core/project.py +++ b/novelwriter/core/project.py @@ -237,12 +237,13 @@ class NWProject(): return - def newProject(self, projData=None): + def newProject(self, projData): """Create a new project by populating the project tree with a few starter items. """ - if projData is None: - projData = {} + if not isinstance(projData, dict): + logger.error("Invalid call to newProject function") + return False popMinimal = projData.get("popMinimal", True) popCustom = projData.get("popCustom", False) @@ -473,8 +474,8 @@ class NWProject(): # 1.2 : Changes the way autoReplace entries are stored. The 1.1 # parser will lose the autoReplace settings if allowed to # read the file. Introduced in version 0.10. - # 1.3 : Reduces the number of layouts to onlye two. One for - # novel documents and one for project notes. Introduced in + # 1.3 : Reduces the number of layouts to only two. One for novel + # documents and one for project notes. Introduced in # version 1.5. if fileVersion not in ("1.0", "1.1", "1.2", "1.3"): @@ -630,8 +631,8 @@ class NWProject(): def saveProject(self, autoSave=False): """Save the project main XML file. The saving command itself - uses a temporary filename, and the file is renamed afterwards to - make sure if the save fails, we're not left with a truncated + uses a temporary filename, and the file is replaced afterwards + to make sure if the save fails, we're not left with a truncated file. """ if self.projPath is None: @@ -720,11 +721,15 @@ class NWProject(): # If we're here, the file was successfully saved, # so let's sort out the temps and backups - if os.path.isfile(backFile): - os.unlink(backFile) - if os.path.isfile(saveFile): - os.rename(saveFile, backFile) - os.rename(tempFile, saveFile) + try: + if os.path.isfile(saveFile): + os.replace(saveFile, backFile) + os.replace(tempFile, saveFile) + except Exception as exc: + self.theParent.makeAlert(self.tr( + "Failed to save project." + ), nwAlert.ERROR, exception=exc) + return False # Save project GUI options self.optState.saveSettings() @@ -857,9 +862,9 @@ class NWProject(): def extractSampleProject(self, projData): """Make a copy of the sample project. - First, try to copy the content of the sample folder to the new - project path, or if the folder doesn't exist, look for the zip - file in the assets folder. + First, look for the sample.zip file in the assets folder and + unpack it. If it doesn't exist, try to copy the content of the + sample folder to the new project path. If neither exits, error. """ projPath = projData.get("projPath", None) if projPath is None: @@ -965,14 +970,14 @@ class NWProject(): return True def setBookTitle(self, bookTitle): - """Set the boom title, that is, the title to include in exports. + """Set the book title, that is, the title to include in exports. """ self.bookTitle = bookTitle.strip() self.setProjectChanged(True) return True def setBookAuthors(self, bookAuthors): - """A line separated list of book authors, parsed into an array. + """A line-separated list of authors, parsed into an array. """ if not isinstance(bookAuthors, str): return False @@ -1097,8 +1102,7 @@ class NWProject(): return True def setAutoReplace(self, autoReplace): - """Update the auto-replace dictionary. This replaces the entire - dictionary, so alterations have to be made in a copy. + """Update the auto-replace dictionary. """ self.autoReplace = autoReplace self.setProjectChanged(True) @@ -1128,7 +1132,7 @@ class NWProject(): ## def getAuthors(self): - """Returns a formatted string of authors. + """Return a formatted string of authors. """ nAuth = len(self.bookAuthors) authString = "" @@ -1225,8 +1229,7 @@ class NWProject(): return it. The variable is cast to a string before lookup. If the word does not exist, it returns itself. """ - theValue = str(theWord) - return self.langData.get(theValue, theValue) + return self.langData.get(str(theWord), str(theWord)) ## # Internal Functions diff --git a/novelwriter/core/spellcheck.py b/novelwriter/core/spellcheck.py index 53cdf11c..00d143d2 100644 --- a/novelwriter/core/spellcheck.py +++ b/novelwriter/core/spellcheck.py @@ -25,7 +25,6 @@ along with this program. If not, see . import os import logging -import novelwriter from novelwriter.error import logException @@ -36,8 +35,6 @@ class NWSpellEnchant(): def __init__(self): - self.mainConf = novelwriter.CONFIG - self._theDict = None self._projDict = set() self._projectDict = None diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 0781202a..dcf34a23 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -40,9 +40,9 @@ class ToHtml(Tokenizer): def __init__(self, theProject): Tokenizer.__init__(self, theProject) - self.genMode = self.M_EXPORT - self.cssStyles = True - self.fullHTML = [] + self._genMode = self.M_EXPORT + self._cssStyles = True + self._fullHTML = [] # Internals self._trMap = {} @@ -50,6 +50,14 @@ class ToHtml(Tokenizer): return + ## + # Properties + ## + + @property + def fullHTML(self): + return self._fullHTML + ## # Setters ## @@ -59,17 +67,17 @@ class ToHtml(Tokenizer): need to make a few changes to formatting, which is managed by these flags. """ - self.genMode = self.M_PREVIEW - self.doKeywords = True - self.doComments = doComments - self.doSynopsis = doSynopsis + self._genMode = self.M_PREVIEW + self._doKeywords = True + self._doComments = doComments + self._doSynopsis = doSynopsis return def setStyles(self, cssStyles): """Enable/disable CSS styling. Some elements may still have class tags. """ - self.cssStyles = cssStyles + self._cssStyles = cssStyles return def setReplaceUnicode(self, doReplace): @@ -93,21 +101,21 @@ class ToHtml(Tokenizer): def getFullResultSize(self): """Return the size of the full HTML result. """ - return sum([len(x) for x in self.fullHTML]) + return sum([len(x) for x in self._fullHTML]) def doPreProcessing(self): """Extend the auto-replace to also properly encode some unicode characters into their respective HTML entities. """ Tokenizer.doPreProcessing(self) - self.theText = self.theText.translate(self._trMap) + self._theText = self._theText.translate(self._trMap) return def doConvert(self): """Convert the list of text tokens into a HTML document saved to theResult. """ - if self.genMode == self.M_PREVIEW: + if self._genMode == self.M_PREVIEW: htmlTags = { # HTML4 + CSS2 (for Qt) self.FMT_B_B: "", self.FMT_B_E: "", @@ -126,7 +134,7 @@ class ToHtml(Tokenizer): self.FMT_D_E: "", } - if self.isNovel and self.genMode != self.M_PREVIEW: + if self._isNovel and self._genMode != self.M_PREVIEW: # For story files, we bump the titles one level up h1Cl = " class='title'" h1 = "h1" @@ -140,13 +148,13 @@ class ToHtml(Tokenizer): h3 = "h3" h4 = "h4" - self.theResult = "" + self._theResult = "" thisPar = [] parStyle = None tmpResult = [] - for tType, tLine, tDirty, tFormat, tStyle in self.theTokens: + for tType, tLine, tDirty, tFormat, tStyle in self._theTokens: # Replace < and > and recompute formatting positions cText = [] @@ -168,7 +176,7 @@ class ToHtml(Tokenizer): # Styles aStyle = [] - if tStyle is not None and self.cssStyles: + if tStyle is not None and self._cssStyles: if tStyle & self.A_LEFT: aStyle.append("text-align: left;") elif tStyle & self.A_RIGHT: @@ -200,7 +208,7 @@ class ToHtml(Tokenizer): else: hStyle = "" - if self.linkHeaders: + if self._linkHeaders: aNm = f"" else: aNm = "" @@ -209,7 +217,7 @@ class ToHtml(Tokenizer): if tType == self.T_EMPTY: if parStyle is None: parStyle = "" - if len(thisPar) > 1 and self.cssStyles: + if len(thisPar) > 1 and self._cssStyles: parClass = " class='break'" else: parClass = "" @@ -257,21 +265,21 @@ class ToHtml(Tokenizer): tTemp = tTemp[:xPos] + htmlTags[xFmt] + tTemp[xPos+xLen:] thisPar.append(tTemp.rstrip()) - elif tType == self.T_SYNOPSIS and self.doSynopsis: + elif tType == self.T_SYNOPSIS and self._doSynopsis: tmpResult.append(self._formatSynopsis(tText)) - elif tType == self.T_COMMENT and self.doComments: + elif tType == self.T_COMMENT and self._doComments: tmpResult.append(self._formatComments(tText)) - elif tType == self.T_KEYWORD and self.doKeywords: + elif tType == self.T_KEYWORD and self._doKeywords: tTemp = f"{self._formatKeywords(tText)}

\n" tmpResult.append(tTemp) - self.theResult = "".join(tmpResult) + self._theResult = "".join(tmpResult) tmpResult = [] - if self.genMode != self.M_PREVIEW: - self.fullHTML.append(self.theResult) + if self._genMode != self.M_PREVIEW: + self._fullHTML.append(self._theResult) return @@ -281,7 +289,7 @@ class ToHtml(Tokenizer): with open(savePath, mode="w", encoding="utf-8") as outFile: theStyle = self.getStyleSheet() theStyle.append("article {width: 800px; margin: 40px auto;}") - bodyText = "".join(self.fullHTML) + bodyText = "".join(self._fullHTML) bodyText = bodyText.replace("\t", " ").rstrip() theHtml = ( @@ -314,24 +322,24 @@ class ToHtml(Tokenizer): """ htmlText = [] tabSpace = spaceChar*nSpaces - for aLine in self.fullHTML: + for aLine in self._fullHTML: htmlText.append(aLine.replace("\t", tabSpace)) - self.fullHTML = htmlText + self._fullHTML = htmlText return def getStyleSheet(self): """Generate a stylesheet appropriate for the current settings. """ theStyles = [] - if not self.cssStyles: + if not self._cssStyles: return theStyles - mScale = self.lineHeight/1.15 - textAlign = "justify" if self.doJustify else "left" + mScale = self._lineHeight/1.15 + textAlign = "justify" if self._doJustify else "left" theStyles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format( - self.textFont, self.textSize + self._textFont, self._textSize )) theStyles.append(( "p {{" @@ -340,9 +348,9 @@ class ToHtml(Tokenizer): "}}" ).format( textAlign, - round(100 * self.lineHeight), - mScale * self.marginText[0], - mScale * self.marginText[1], + round(100 * self._lineHeight), + mScale * self._marginText[0], + mScale * self._marginText[1], )) theStyles.append(( "h1 {{" @@ -352,7 +360,7 @@ class ToHtml(Tokenizer): "margin-bottom: {1:.2f}em;" "}}" ).format( - mScale * self.marginHead1[0], mScale * self.marginHead1[1] + mScale * self._marginHead1[0], mScale * self._marginHead1[1] )) theStyles.append(( "h2 {{" @@ -362,7 +370,7 @@ class ToHtml(Tokenizer): "margin-bottom: {1:.2f}em;" "}}" ).format( - mScale * self.marginHead2[0], mScale * self.marginHead2[1] + mScale * self._marginHead2[0], mScale * self._marginHead2[1] )) theStyles.append(( "h3 {{" @@ -372,7 +380,7 @@ class ToHtml(Tokenizer): "margin-bottom: {1:.2f}em;" "}}" ).format( - mScale * self.marginHead3[0], mScale * self.marginHead3[1] + mScale * self._marginHead3[0], mScale * self._marginHead3[1] )) theStyles.append(( "h4 {{" @@ -382,7 +390,7 @@ class ToHtml(Tokenizer): "margin-bottom: {1:.2f}em;" "}}" ).format( - mScale * self.marginHead4[0], mScale * self.marginHead4[1] + mScale * self._marginHead4[0], mScale * self._marginHead4[1] )) theStyles.append(( ".title {{" @@ -391,7 +399,7 @@ class ToHtml(Tokenizer): "margin-bottom: {1:.2f}em;" "}}" ).format( - mScale * self.marginTitle[0], mScale * self.marginTitle[1] + mScale * self._marginTitle[0], mScale * self._marginTitle[1] )) theStyles.append(( ".sep, .skip {{" @@ -418,7 +426,7 @@ class ToHtml(Tokenizer): def _formatSynopsis(self, tText): """Apply HTML formatting to synopsis. """ - if self.genMode == self.M_PREVIEW: + if self._genMode == self.M_PREVIEW: sSynop = self._trSynopsis return f"

{sSynop}: {tText}

\n" else: @@ -428,7 +436,7 @@ class ToHtml(Tokenizer): def _formatComments(self, tText): """Apply HTML formatting to comments. """ - if self.genMode == self.M_PREVIEW: + if self._genMode == self.M_PREVIEW: return f"

{tText}

\n" else: sComm = self._localLookup("Comment") @@ -449,7 +457,7 @@ class ToHtml(Tokenizer): if theBits[0] == nwKeyWords.TAG_KEY: retText += f"{theBits[1]}" else: - if self.genMode == self.M_PREVIEW: + if self._genMode == self.M_PREVIEW: for tTag in theBits[1:]: refTags.append(f"{tTag}") retText += ", ".join(refTags) diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index 4a37b700..44077a33 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -85,62 +85,62 @@ class Tokenizer(): self.mainConf = novelwriter.CONFIG # Data Variables - self.theText = "" # The raw text to be tokenized - self.theHandle = None # The handle associated with the text - self.theItem = None # The NWItem associated with the handle - self.theTokens = [] # The list of the processed tokens - self.theResult = "" # The result of the last document + self._theText = "" # The raw text to be tokenized + self._theHandle = None # The handle associated with the text + self._theItem = None # The NWItem associated with the handle + self._theTokens = [] # The list of the processed tokens + self._theResult = "" # The result of the last document - self.keepMarkdown = False # Whether to keep the markdown text - self.theMarkdown = [] # The result novelWriter markdown of all documents + self._keepMarkdown = False # Whether to keep the markdown text + self._theMarkdown = [] # The result novelWriter markdown of all documents # User Settings - self.textFont = "Serif" # Output text font - self.textSize = 11 # Output text size - self.textFixed = False # Fixed width text - self.lineHeight = 1.15 # Line height in units of em - self.blockIndent = 4.00 # Block indent in units of em - self.doJustify = False # Justify text - self.doBodyText = True # Include body text - self.doSynopsis = False # Also process synopsis comments - self.doComments = False # Also process comments - self.doKeywords = False # Also process keywords like tags and references + self._textFont = "Serif" # Output text font + self._textSize = 11 # Output text size + self._textFixed = False # Fixed width text + self._lineHeight = 1.15 # Line height in units of em + self._blockIndent = 4.00 # Block indent in units of em + self._doJustify = False # Justify text + self._doBodyText = True # Include body text + self._doSynopsis = False # Also process synopsis comments + self._doComments = False # Also process comments + self._doKeywords = False # Also process keywords like tags and references # Margins - self.marginTitle = (1.000, 0.500) - self.marginHead1 = (1.000, 0.500) - self.marginHead2 = (0.834, 0.500) - self.marginHead3 = (0.584, 0.500) - self.marginHead4 = (0.584, 0.500) - self.marginText = (0.000, 0.584) - self.marginMeta = (0.000, 0.584) + self._marginTitle = (1.000, 0.500) + self._marginHead1 = (1.000, 0.500) + self._marginHead2 = (0.834, 0.500) + self._marginHead3 = (0.584, 0.500) + self._marginHead4 = (0.584, 0.500) + self._marginText = (0.000, 0.584) + self._marginMeta = (0.000, 0.584) # Title Formats - self.fmtTitle = "%title%" # Formatting for titles - self.fmtChapter = "%title%" # Formatting for numbered chapters - self.fmtUnNum = "%title%" # Formatting for unnumbered chapters - self.fmtScene = "%title%" # Formatting for scenes - self.fmtSection = "%title%" # Formatting for sections + self._fmtTitle = "%title%" # Formatting for titles + self._fmtChapter = "%title%" # Formatting for numbered chapters + self._fmtUnNum = "%title%" # Formatting for unnumbered chapters + self._fmtScene = "%title%" # Formatting for scenes + self._fmtSection = "%title%" # Formatting for sections - self.hideScene = False # Do not include scene headers - self.hideSection = False # Do not include section headers + self._hideScene = False # Do not include scene headers + self._hideSection = False # Do not include section headers - self.linkHeaders = False # Add an anchor before headers + self._linkHeaders = False # Add an anchor before headers # Instance Variables - self.numChapter = 0 # Counter for chapter numbers - self.numChScene = 0 # Counter for scene number within chapter - self.numAbsScene = 0 # Counter for scene number within novel - self.firstScene = False # Flag to indicate that the first scene of the chapter + self._numChapter = 0 # Counter for chapter numbers + self._numChScene = 0 # Counter for scene number within chapter + self._numAbsScene = 0 # Counter for scene number within novel + self._firstScene = False # Flag to indicate that the first scene of the chapter # This File - self.isNone = False # Document has unknown layout - self.isNovel = False # Document is a novel document - self.isNote = False # Document is a project note - self.isFirst = True # Document is the first in a set + self._isNone = False # Document has unknown layout + self._isNovel = False # Document is a novel document + self._isNote = False # Document is a project note + self._isFirst = True # Document is the first in a set # Error Handling - self.errData = [] + self._errData = [] # Function Mapping self._localLookup = self.theProject.localLookup @@ -151,100 +151,116 @@ class Tokenizer(): return + ## + # Properties + ## + + @property + def theResult(self): + return self._theResult + + @property + def theMarkdown(self): + return self._theMarkdown + + @property + def errData(self): + return self._errData + ## # Setters ## def setTitleFormat(self, fmtTitle): - self.fmtTitle = fmtTitle.strip() + self._fmtTitle = fmtTitle.strip() return def setChapterFormat(self, fmtChapter): - self.fmtChapter = fmtChapter.strip() + self._fmtChapter = fmtChapter.strip() return def setUnNumberedFormat(self, fmtUnNum): - self.fmtUnNum = fmtUnNum.strip() + self._fmtUnNum = fmtUnNum.strip() return def setSceneFormat(self, fmtScene, hideScene): - self.fmtScene = fmtScene.strip() - self.hideScene = hideScene + self._fmtScene = fmtScene.strip() + self._hideScene = hideScene return def setSectionFormat(self, fmtSection, hideSection): - self.fmtSection = fmtSection.strip() - self.hideSection = hideSection + self._fmtSection = fmtSection.strip() + self._hideSection = hideSection return def setFont(self, textFont, textSize, textFixed=False): - self.textFont = textFont - self.textSize = round(int(textSize)) - self.textFixed = textFixed + self._textFont = textFont + self._textSize = round(int(textSize)) + self._textFixed = textFixed return def setLineHeight(self, lineHeight): - self.lineHeight = min(max(float(lineHeight), 0.5), 5.0) + self._lineHeight = min(max(float(lineHeight), 0.5), 5.0) return def setBlockIndent(self, blockIndent): - self.blockIndent = min(max(float(blockIndent), 0.0), 10.0) + self._blockIndent = min(max(float(blockIndent), 0.0), 10.0) return def setJustify(self, doJustify): - self.doJustify = doJustify + self._doJustify = doJustify return def setTitleMargins(self, mUpper, mLower): - self.marginTitle = (float(mUpper), float(mLower)) + self._marginTitle = (float(mUpper), float(mLower)) return def setHead1Margins(self, mUpper, mLower): - self.marginHead1 = (float(mUpper), float(mLower)) + self._marginHead1 = (float(mUpper), float(mLower)) return def setHead2Margins(self, mUpper, mLower): - self.marginHead2 = (float(mUpper), float(mLower)) + self._marginHead2 = (float(mUpper), float(mLower)) return def setHead3Margins(self, mUpper, mLower): - self.marginHead3 = (float(mUpper), float(mLower)) + self._marginHead3 = (float(mUpper), float(mLower)) return def setHead4Margins(self, mUpper, mLower): - self.marginHead4 = (float(mUpper), float(mLower)) + self._marginHead4 = (float(mUpper), float(mLower)) return def setTextMargins(self, mUpper, mLower): - self.marginText = (float(mUpper), float(mLower)) + self._marginText = (float(mUpper), float(mLower)) return def setMetaMargins(self, mUpper, mLower): - self.marginMeta = (float(mUpper), float(mLower)) + self._marginMeta = (float(mUpper), float(mLower)) return def setLinkHeaders(self, linkHeaders): - self.linkHeaders = linkHeaders + self._linkHeaders = linkHeaders return def setBodyText(self, doBodyText): - self.doBodyText = doBodyText + self._doBodyText = doBodyText return def setSynopsis(self, doSynopsis): - self.doSynopsis = doSynopsis + self._doSynopsis = doSynopsis return def setComments(self, doComments): - self.doComments = doComments + self._doComments = doComments return def setKeywords(self, doKeywords): - self.doKeywords = doKeywords + self._doKeywords = doKeywords return def setKeepMarkdown(self, keepMarkdown): - self.keepMarkdown = keepMarkdown + self._keepMarkdown = keepMarkdown return ## @@ -261,20 +277,20 @@ class Tokenizer(): if theItem.itemType != nwItemType.ROOT: return False - if self.isFirst: + if self._isFirst: textAlign = self.A_CENTRE - self.isFirst = False + self._isFirst = False else: textAlign = self.A_PBB | self.A_CENTRE locNotes = self._localLookup("Notes") theTitle = f"{locNotes}: {theItem.itemName}" - self.theTokens = [] - self.theTokens.append(( + self._theTokens = [] + self._theTokens.append(( self.T_TITLE, 0, theTitle, None, textAlign )) - if self.keepMarkdown: - self.theMarkdown.append(f"# {theTitle}\n\n") + if self._keepMarkdown: + self._theMarkdown.append(f"# {theTitle}\n\n") return True @@ -282,33 +298,33 @@ class Tokenizer(): """Set the text for the tokenizer from a handle. If theText is not set, load it from the file. """ - self.theHandle = theHandle - self.theItem = self.theProject.projTree[theHandle] - if self.theItem is None: + self._theHandle = theHandle + self._theItem = self.theProject.projTree[theHandle] + if self._theItem is None: return False - self.theText = "" + self._theText = "" if theText is not None: # If the text is set, just use that - self.theText = theText + self._theText = theText else: # Otherwise, load it from file theDoc = NWDoc(self.theProject, theHandle) theText = theDoc.readDocument() if theText: - self.theText = theText + self._theText = theText - docSize = len(self.theText) + docSize = len(self._theText) if docSize > nwConst.MAX_DOCSIZE: errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format( - self.theItem.itemName, f"{docSize/1.0e6:.2f}" + self._theItem.itemName, f"{docSize/1.0e6:.2f}" ) - self.theText = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal) - self.errData.append(errVal) + self._theText = "# {0}\n\n{1}\n\n".format(self.tr("ERROR"), errVal) + self._errData.append(errVal) - self.isNone = self.theItem.itemLayout == nwItemLayout.NO_LAYOUT - self.isNovel = self.theItem.itemLayout == nwItemLayout.DOCUMENT - self.isNote = self.theItem.itemLayout == nwItemLayout.NOTE + self._isNone = self._theItem.itemLayout == nwItemLayout.NO_LAYOUT + self._isNovel = self._theItem.itemLayout == nwItemLayout.DOCUMENT + self._isNote = self._theItem.itemLayout == nwItemLayout.NOTE return True @@ -321,11 +337,11 @@ class Tokenizer(): for aKey, aVal in self.theProject.autoReplace.items(): repDict[f"<{aKey}>"] = aVal xRep = re.compile("|".join([re.escape(k) for k in repDict.keys()]), flags=re.DOTALL) - self.theText = xRep.sub(lambda x: repDict[x.group(0)], self.theText) + self._theText = xRep.sub(lambda x: repDict[x.group(0)], self._theText) # Process the character translation map trDict = {nwUnicode.U_MAPOSS: nwUnicode.U_RSQUO} - self.theText = self.theText.translate(str.maketrans(trDict)) + self._theText = self._theText.translate(str.maketrans(trDict)) return @@ -341,8 +357,8 @@ class Tokenizer(): escReplace = re.compile( "|".join([re.escape(k) for k in escapeDict.keys()]), flags=re.DOTALL ) - self.theResult = escReplace.sub( - lambda x: escapeDict[x.group(0)], self.theResult + self._theResult = escReplace.sub( + lambda x: escapeDict[x.group(0)], self._theResult ) return @@ -356,7 +372,7 @@ class Tokenizer(): The format of the token list is an entry with a five-tuple for each line in the file. The tuple is as follows: 1: The type of the block, self.T_* - 2: The line in file where this block occurred + 2: The line in the file where this block occurred 3: The text content of the block, without leading tags 4: The internal formatting map of the text, self.FMT_* 5: The style of the block, self.A_* @@ -368,20 +384,20 @@ class Tokenizer(): (QRegularExpression(nwRegEx.FMT_ST), [None, self.FMT_D_B, None, self.FMT_D_E]), ] - self.theTokens = [] + self._theTokens = [] tmpMarkdown = [] nLine = 0 breakNext = False - for aLine in self.theText.splitlines(): + for aLine in self._theText.splitlines(): nLine += 1 sLine = aLine.strip() # Check for blank lines if len(sLine) == 0: - self.theTokens.append(( + self._theTokens.append(( self.T_EMPTY, nLine, "", None, self.A_NONE )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("\n") continue @@ -403,7 +419,7 @@ class Tokenizer(): continue elif sLine == "[VSPACE]": - self.theTokens.append( + self._theTokens.append( (self.T_SKIP, nLine, "", None, sAlign) ) continue @@ -411,11 +427,11 @@ class Tokenizer(): elif sLine.startswith("[VSPACE:") and sLine.endswith("]"): nSkip = checkInt(sLine[8:-1], 0) if nSkip >= 1: - self.theTokens.append( + self._theTokens.append( (self.T_SKIP, nLine, "", None, sAlign) ) if nSkip > 1: - self.theTokens += (nSkip - 1) * [ + self._theTokens += (nSkip - 1) * [ (self.T_SKIP, nLine, "", None, self.A_NONE) ] continue @@ -424,87 +440,87 @@ class Tokenizer(): cLine = aLine[1:].lstrip() synTag = cLine[:9].lower() if synTag == "synopsis:": - self.theTokens.append(( + self._theTokens.append(( self.T_SYNOPSIS, nLine, cLine[9:].strip(), None, sAlign )) - if self.doSynopsis and self.keepMarkdown: + if self._doSynopsis and self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) else: - self.theTokens.append(( + self._theTokens.append(( self.T_COMMENT, nLine, aLine[1:].strip(), None, sAlign )) - if self.doComments and self.keepMarkdown: + if self._doComments and self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[0] == "@": - self.theTokens.append(( + self._theTokens.append(( self.T_KEYWORD, nLine, aLine[1:].strip(), None, sAlign )) - if self.doKeywords and self.keepMarkdown: + if self._doKeywords and self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:2] == "# ": - if self.isNovel: + if self._isNovel: sAlign |= self.A_CENTRE sAlign |= self.A_PBB - self.theTokens.append(( + self._theTokens.append(( self.T_HEAD1, nLine, aLine[2:].strip(), None, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:3] == "## ": - if self.isNovel: + if self._isNovel: sAlign |= self.A_PBB - self.theTokens.append(( + self._theTokens.append(( self.T_HEAD2, nLine, aLine[3:].strip(), None, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:4] == "### ": - self.theTokens.append(( + self._theTokens.append(( self.T_HEAD3, nLine, aLine[4:].strip(), None, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:5] == "#### ": - self.theTokens.append(( + self._theTokens.append(( self.T_HEAD4, nLine, aLine[5:].strip(), None, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:3] == "#! ": - if self.isNovel: + if self._isNovel: tStyle = self.T_TITLE else: tStyle = self.T_HEAD1 - self.theTokens.append(( + self._theTokens.append(( tStyle, nLine, aLine[3:].strip(), None, sAlign | self.A_CENTRE )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) elif aLine[:4] == "##! ": - if self.isNovel: + if self._isNovel: tStyle = self.T_UNNUM sAlign |= self.A_PBB else: tStyle = self.T_HEAD2 - self.theTokens.append(( + self._theTokens.append(( tStyle, nLine, aLine[4:].strip(), None, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) else: - if not self.doBodyText: + if not self._doBodyText: # Skip all body text continue @@ -554,33 +570,33 @@ class Tokenizer(): # Save the line as is, but append the array of formatting locations # sorted by position fmtPos = sorted(fmtPos, key=itemgetter(0)) - self.theTokens.append(( + self._theTokens.append(( self.T_TEXT, nLine, aLine, fmtPos, sAlign )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("%s\n" % aLine) # If we have content, turn off the first page flag - if self.isFirst and self.theTokens: - self.isFirst = False + if self._isFirst and self._theTokens: + self._isFirst = False # Make sure the token array doesn't start with a page break # on the very first page, adding a blank first page. - if self.theTokens[0][4] & self.A_PBB: - tToken = self.theTokens[0] - self.theTokens[0] = ( + if self._theTokens[0][4] & self.A_PBB: + tToken = self._theTokens[0] + self._theTokens[0] = ( tToken[0], tToken[1], tToken[2], tToken[3], tToken[4] & ~self.A_PBB ) # Always add an empty line at the end of the file - self.theTokens.append(( + self._theTokens.append(( self.T_EMPTY, nLine, "", None, self.A_NONE )) - if self.keepMarkdown: + if self._keepMarkdown: tmpMarkdown.append("\n") - if self.keepMarkdown: - self.theMarkdown.append("".join(tmpMarkdown)) + if self._keepMarkdown: + self._theMarkdown.append("".join(tmpMarkdown)) # Second Pass # =========== @@ -588,13 +604,13 @@ class Tokenizer(): pToken = (self.T_EMPTY, 0, "", None, self.A_NONE) nToken = (self.T_EMPTY, 0, "", None, self.A_NONE) - tCount = len(self.theTokens) - for n, tToken in enumerate(self.theTokens): + tCount = len(self._theTokens) + for n, tToken in enumerate(self._theTokens): if n > 0: - pToken = self.theTokens[n-1] + pToken = self._theTokens[n-1] if n < tCount - 1: - nToken = self.theTokens[n+1] + nToken = self._theTokens[n+1] if tToken[0] == self.T_KEYWORD: aStyle = tToken[4] @@ -602,7 +618,7 @@ class Tokenizer(): aStyle |= self.A_Z_TOPMRG if nToken[0] == self.T_KEYWORD: aStyle |= self.A_Z_BTMMRG - self.theTokens[n] = ( + self._theTokens[n] = ( tToken[0], tToken[1], tToken[2], tToken[3], aStyle ) @@ -612,20 +628,20 @@ class Tokenizer(): """Apply formatting to the text headers for novel files. This also applies chapter and scene numbering. """ - if not self.isNovel: + if not self._isNovel: return False - for n, tToken in enumerate(self.theTokens): + for n, tToken in enumerate(self._theTokens): # In case we see text before a scene, we reset the flag if tToken[0] == self.T_TEXT: - self.firstScene = False + self._firstScene = False elif tToken[0] == self.T_HEAD1: # Partition - tTemp = self._formatHeading(self.fmtTitle, tToken[2]) - self.theTokens[n] = ( + tTemp = self._formatHeading(self._fmtTitle, tToken[2]) + self._theTokens[n] = ( tToken[0], tToken[1], tTemp, None, tToken[4] ) @@ -634,75 +650,75 @@ class Tokenizer(): # Numbered or Unnumbered if tToken[0] == self.T_UNNUM: - tTemp = self._formatHeading(self.fmtUnNum, tToken[2]) + tTemp = self._formatHeading(self._fmtUnNum, tToken[2]) else: - self.numChapter += 1 - tTemp = self._formatHeading(self.fmtChapter, tToken[2]) + self._numChapter += 1 + tTemp = self._formatHeading(self._fmtChapter, tToken[2]) # Format the chapter header - self.theTokens[n] = ( + self._theTokens[n] = ( tToken[0], tToken[1], tTemp, None, tToken[4] ) # Set scene variables - self.firstScene = True - self.numChScene = 0 + self._firstScene = True + self._numChScene = 0 elif tToken[0] == self.T_HEAD3: # Scene - self.numChScene += 1 - self.numAbsScene += 1 + self._numChScene += 1 + self._numAbsScene += 1 - tTemp = self._formatHeading(self.fmtScene, tToken[2]) - if tTemp == "" and self.hideScene: - self.theTokens[n] = ( + tTemp = self._formatHeading(self._fmtScene, tToken[2]) + if tTemp == "" and self._hideScene: + self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE ) - elif tTemp == "" and not self.hideScene: - if self.firstScene: - self.theTokens[n] = ( + elif tTemp == "" and not self._hideScene: + if self._firstScene: + self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE ) else: - self.theTokens[n] = ( + self._theTokens[n] = ( self.T_SKIP, tToken[1], "", None, tToken[4] ) - elif tTemp == self.fmtScene: - if self.firstScene: - self.theTokens[n] = ( + elif tTemp == self._fmtScene: + if self._firstScene: + self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE ) else: - self.theTokens[n] = ( + self._theTokens[n] = ( self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE ) else: - self.theTokens[n] = ( + self._theTokens[n] = ( tToken[0], tToken[1], tTemp, None, tToken[4] ) # Definitely no longer the first scene - self.firstScene = False + self._firstScene = False elif tToken[0] == self.T_HEAD4: # Section - tTemp = self._formatHeading(self.fmtSection, tToken[2]) - if tTemp == "" and self.hideSection: - self.theTokens[n] = ( + tTemp = self._formatHeading(self._fmtSection, tToken[2]) + if tTemp == "" and self._hideSection: + self._theTokens[n] = ( self.T_EMPTY, tToken[1], "", None, self.A_NONE ) - elif tTemp == "" and not self.hideSection: - self.theTokens[n] = ( + elif tTemp == "" and not self._hideSection: + self._theTokens[n] = ( self.T_SKIP, tToken[1], "", None, tToken[4] ) - elif tTemp == self.fmtSection: - self.theTokens[n] = ( + elif tTemp == self._fmtSection: + self._theTokens[n] = ( self.T_SEP, tToken[1], tTemp, None, tToken[4] | self.A_CENTRE ) else: - self.theTokens[n] = ( + self._theTokens[n] = ( tToken[0], tToken[1], tTemp, None, tToken[4] ) @@ -712,7 +728,7 @@ class Tokenizer(): """Save the data to a plain text file. """ with open(savePath, mode="w", encoding="utf-8") as outFile: - for nwdPage in self.theMarkdown: + for nwdPage in self._theMarkdown: outFile.write(nwdPage) return @@ -724,15 +740,15 @@ class Tokenizer(): """Replaces the %keyword% strings. """ theTitle = theTitle.replace(r"%title%", theText) - theTitle = theTitle.replace(r"%ch%", str(self.numChapter)) - theTitle = theTitle.replace(r"%sc%", str(self.numChScene)) - theTitle = theTitle.replace(r"%sca%", str(self.numAbsScene)) + theTitle = theTitle.replace(r"%ch%", str(self._numChapter)) + theTitle = theTitle.replace(r"%sc%", str(self._numChScene)) + theTitle = theTitle.replace(r"%sca%", str(self._numAbsScene)) if r"%chw%" in theTitle: - theTitle = theTitle.replace(r"%chw%", self._localLookup(self.numChapter)) + theTitle = theTitle.replace(r"%chw%", self._localLookup(self._numChapter)) if r"%chi%" in theTitle: - theTitle = theTitle.replace(r"%chi%", numberToRoman(self.numChapter, True)) + theTitle = theTitle.replace(r"%chi%", numberToRoman(self._numChapter, True)) if r"%chI%" in theTitle: - theTitle = theTitle.replace(r"%chI%", numberToRoman(self.numChapter, False)) + theTitle = theTitle.replace(r"%chI%", numberToRoman(self._numChapter, False)) return theTitle[:1].upper() + theTitle[1:] diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py index ff982868..95218b7c 100644 --- a/novelwriter/core/tomd.py +++ b/novelwriter/core/tomd.py @@ -39,21 +39,29 @@ class ToMarkdown(Tokenizer): def __init__(self, theProject): Tokenizer.__init__(self, theProject) - self.genMode = self.M_STD - self.fullMD = [] + self._genMode = self.M_STD + self._fullMD = [] return + ## + # Properties + ## + + @property + def fullMD(self): + return self._fullMD + ## # Setters ## def setStandardMarkdown(self): - self.genMode = self.M_STD + self._genMode = self.M_STD return def setGitHubMarkdown(self): - self.genMode = self.M_GH + self._genMode = self.M_GH return ## @@ -63,13 +71,13 @@ class ToMarkdown(Tokenizer): def getFullResultSize(self): """Return the size of the full Markdown result. """ - return sum([len(x) for x in self.fullMD]) + return sum([len(x) for x in self._fullMD]) def doConvert(self): """Convert the list of text tokens into a HTML document saved to theResult. """ - if self.genMode == self.M_STD: + if self._genMode == self.M_STD: # Standard mdTags = { self.FMT_B_B: "**", @@ -90,12 +98,12 @@ class ToMarkdown(Tokenizer): self.FMT_D_E: "~~", } - self.theResult = "" + self._theResult = "" thisPar = [] tmpResult = [] - for tType, _, tText, tFormat, tStyle in self.theTokens: + for tType, _, tText, tFormat, tStyle in self._theTokens: # Process Text Type if tType == self.T_EMPTY: @@ -140,21 +148,21 @@ class ToMarkdown(Tokenizer): tTemp = tTemp[:xPos] + mdTags[xFmt] + tTemp[xPos+xLen:] thisPar.append(tTemp.rstrip()) - elif tType == self.T_SYNOPSIS and self.doSynopsis: + elif tType == self.T_SYNOPSIS and self._doSynopsis: locName = self._localLookup("Synopsis") tmpResult.append(f"**{locName}:** {tText}\n\n") - elif tType == self.T_COMMENT and self.doComments: + elif tType == self.T_COMMENT and self._doComments: locName = self._localLookup("Comment") tmpResult.append(f"**{locName}:** {tText}\n\n") - elif tType == self.T_KEYWORD and self.doKeywords: + elif tType == self.T_KEYWORD and self._doKeywords: tmpResult.append(self._formatKeywords(tText, tStyle)) - self.theResult = "".join(tmpResult) + self._theResult = "".join(tmpResult) tmpResult = [] - self.fullMD.append(self.theResult) + self._fullMD.append(self._theResult) return @@ -162,7 +170,7 @@ class ToMarkdown(Tokenizer): """Save the data to a plain text file. """ with open(savePath, mode="w", encoding="utf-8") as outFile: - theText = "".join(self.fullMD) + theText = "".join(self._fullMD) outFile.write(theText) return @@ -172,10 +180,10 @@ class ToMarkdown(Tokenizer): """ fullMD = [] eightSpace = spaceChar*nSpaces - for aPage in self.fullMD: + for aPage in self._fullMD: fullMD.append(aPage.replace("\t", eightSpace)) - self.fullMD = fullMD + self._fullMD = fullMD return ## diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index 4db138e7..75807a7f 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -115,27 +115,27 @@ class ToOdt(Tokenizer): self._errData = [] # List of errors encountered # Properties - self.textFont = "Liberation Serif" - self.textSize = 12 - self.textFixed = False - self.colourHead = False - self.headerText = "" + self._textFont = "Liberation Serif" + self._textSize = 12 + self._textFixed = False + self._colourHead = False + self._headerText = "" # Internal - self._fontFamily = "'Liberation Serif'" - self._fontPitch = "variable" - self._fSizeTitle = "30pt" - self._fSizeHead1 = "24pt" - self._fSizeHead2 = "20pt" - self._fSizeHead3 = "16pt" - self._fSizeHead4 = "14pt" - self._fSizeHead = "14pt" - self._fSizeText = "12pt" - self._lineHeight = "115%" - self._blockIndent = "1.693cm" - self._textAlign = "left" - self._dLanguage = "en" - self._dCountry = "GB" + self._fontFamily = "'Liberation Serif'" + self._fontPitch = "variable" + self._fSizeTitle = "30pt" + self._fSizeHead1 = "24pt" + self._fSizeHead2 = "20pt" + self._fSizeHead3 = "16pt" + self._fSizeHead4 = "14pt" + self._fSizeHead = "14pt" + self._fSizeText = "12pt" + self._fLineHeight = "115%" + self._fBlockIndent = "1.693cm" + self._textAlign = "left" + self._dLanguage = "en" + self._dCountry = "GB" # Text Margings in Units of em self._mTopTitle = "0.423cm" @@ -192,7 +192,7 @@ class ToOdt(Tokenizer): def setColourHeaders(self, doColour): """Enable/disable coloured headings and comments. """ - self.colourHead = doColour + self._colourHead = doColour return ## @@ -209,40 +209,40 @@ class ToOdt(Tokenizer): # Initialise Variables # ==================== - self._fontFamily = self.textFont - if len(self.textFont.split()) > 1: - self._fontFamily = f"'{self.textFont}'" - self._fontPitch = "fixed" if self.textFixed else "variable" + self._fontFamily = self._textFont + if len(self._textFont.split()) > 1: + self._fontFamily = f"'{self._textFont}'" + self._fontPitch = "fixed" if self._textFixed else "variable" - self._fSizeTitle = f"{round(2.50 * self.textSize):d}pt" - self._fSizeHead1 = f"{round(2.00 * self.textSize):d}pt" - self._fSizeHead2 = f"{round(1.60 * self.textSize):d}pt" - self._fSizeHead3 = f"{round(1.30 * self.textSize):d}pt" - self._fSizeHead4 = f"{round(1.15 * self.textSize):d}pt" - self._fSizeHead = f"{round(1.15 * self.textSize):d}pt" - self._fSizeText = f"{self.textSize:d}pt" + self._fSizeTitle = f"{round(2.50 * self._textSize):d}pt" + self._fSizeHead1 = f"{round(2.00 * self._textSize):d}pt" + self._fSizeHead2 = f"{round(1.60 * self._textSize):d}pt" + self._fSizeHead3 = f"{round(1.30 * self._textSize):d}pt" + self._fSizeHead4 = f"{round(1.15 * self._textSize):d}pt" + self._fSizeHead = f"{round(1.15 * self._textSize):d}pt" + self._fSizeText = f"{self._textSize:d}pt" - mScale = self.lineHeight/1.15 + mScale = self._lineHeight/1.15 - self._mTopTitle = self._emToCm(mScale * self.marginTitle[0]) - self._mTopHead1 = self._emToCm(mScale * self.marginHead1[0]) - self._mTopHead2 = self._emToCm(mScale * self.marginHead2[0]) - self._mTopHead3 = self._emToCm(mScale * self.marginHead3[0]) - self._mTopHead4 = self._emToCm(mScale * self.marginHead4[0]) - self._mTopHead = self._emToCm(mScale * self.marginHead4[0]) - self._mTopText = self._emToCm(mScale * self.marginText[0]) - self._mTopMeta = self._emToCm(mScale * self.marginMeta[0]) + self._mTopTitle = self._emToCm(mScale * self._marginTitle[0]) + self._mTopHead1 = self._emToCm(mScale * self._marginHead1[0]) + self._mTopHead2 = self._emToCm(mScale * self._marginHead2[0]) + self._mTopHead3 = self._emToCm(mScale * self._marginHead3[0]) + self._mTopHead4 = self._emToCm(mScale * self._marginHead4[0]) + self._mTopHead = self._emToCm(mScale * self._marginHead4[0]) + self._mTopText = self._emToCm(mScale * self._marginText[0]) + self._mTopMeta = self._emToCm(mScale * self._marginMeta[0]) - self._mBotTitle = self._emToCm(mScale * self.marginTitle[1]) - self._mBotHead1 = self._emToCm(mScale * self.marginHead1[1]) - self._mBotHead2 = self._emToCm(mScale * self.marginHead2[1]) - self._mBotHead3 = self._emToCm(mScale * self.marginHead3[1]) - self._mBotHead4 = self._emToCm(mScale * self.marginHead4[1]) - self._mBotHead = self._emToCm(mScale * self.marginHead4[1]) - self._mBotText = self._emToCm(mScale * self.marginText[1]) - self._mBotMeta = self._emToCm(mScale * self.marginMeta[1]) + self._mBotTitle = self._emToCm(mScale * self._marginTitle[1]) + self._mBotHead1 = self._emToCm(mScale * self._marginHead1[1]) + self._mBotHead2 = self._emToCm(mScale * self._marginHead2[1]) + self._mBotHead3 = self._emToCm(mScale * self._marginHead3[1]) + self._mBotHead4 = self._emToCm(mScale * self._marginHead4[1]) + self._mBotHead = self._emToCm(mScale * self._marginHead4[1]) + self._mBotText = self._emToCm(mScale * self._marginText[1]) + self._mBotMeta = self._emToCm(mScale * self._marginMeta[1]) - if self.colourHead: + if self._colourHead: self._colHead12 = "#2a6099" self._opaHead12 = "100%" self._colHead34 = "#444444" @@ -250,9 +250,9 @@ class ToOdt(Tokenizer): self._colMetaTx = "#813709" self._opaMetaTx = "100%" - self._lineHeight = f"{round(100 * self.lineHeight):d}%" - self._blockIndent = self._emToCm(self.blockIndent) - self._textAlign = "justify" if self.doJustify else "left" + self._fLineHeight = f"{round(100 * self._lineHeight):d}%" + self._fBlockIndent = self._emToCm(self._blockIndent) + self._textAlign = "justify" if self._doJustify else "left" # Clear Errors self._errData = [] @@ -260,10 +260,10 @@ class ToOdt(Tokenizer): # Document Header # =============== - if self.headerText == "": + if self._headerText == "": theTitle = self.theProject.bookTitle theAuth = self.theProject.getAuthors() - self.headerText = f"{theTitle} / {theAuth} /" + self._headerText = f"{theTitle} / {theAuth} /" # Create Roots # ============ @@ -272,7 +272,7 @@ class ToOdt(Tokenizer): tAttr[_mkTag("office", "version")] = X_VERS fAttr = {} - fAttr[_mkTag("style", "name")] = self.textFont + fAttr[_mkTag("style", "name")] = self._textFont fAttr[_mkTag("style", "font-pitch")] = self._fontPitch if self._isFlat: @@ -345,7 +345,7 @@ class ToOdt(Tokenizer): def doConvert(self): """Convert the list of text tokens into XML elements. """ - self.theResult = "" # Not used, but cleared just in case + self._theResult = "" # Not used, but cleared just in case odtTags = { self.FMT_B_B: "_B", # Bold open format @@ -359,7 +359,7 @@ class ToOdt(Tokenizer): thisPar = [] thisFmt = [] parStyle = None - for tType, _, tText, tFormat, tStyle in self.theTokens: + for tType, _, tText, tFormat, tStyle in self._theTokens: # Styles oStyle = ODTParagraphStyle() @@ -385,14 +385,14 @@ class ToOdt(Tokenizer): oStyle.setMarginTop("0.000cm") if tStyle & self.A_IND_L: - oStyle.setMarginLeft(self._blockIndent) + oStyle.setMarginLeft(self._fBlockIndent) if tStyle & self.A_IND_R: - oStyle.setMarginRight(self._blockIndent) + oStyle.setMarginRight(self._fBlockIndent) # Process Text Types if tType == self.T_EMPTY: if len(thisPar) > 1 and parStyle is not None: - if self.doJustify: + if self._doJustify: parStyle.setTextAlign("left") if len(thisPar) > 0: @@ -449,15 +449,15 @@ class ToOdt(Tokenizer): thisPar.append(tTxt) thisFmt.append(tFmt) - elif tType == self.T_SYNOPSIS and self.doSynopsis: + elif tType == self.T_SYNOPSIS and self._doSynopsis: tTemp, fTemp = self._formatSynopsis(tText) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) - elif tType == self.T_COMMENT and self.doComments: + elif tType == self.T_COMMENT and self._doComments: tTemp, fTemp = self._formatComments(tText) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) - elif tType == self.T_KEYWORD and self.doKeywords: + elif tType == self.T_KEYWORD and self._doKeywords: tTemp, fTemp = self._formatKeywords(tText) self._addTextPar("Text_Meta", oStyle, tTemp, theFmt=fTemp) @@ -696,7 +696,7 @@ class ToOdt(Tokenizer): def _emToCm(self, emVal): """Converts an em value to centimetres. """ - return f"{emVal*2.54/72*self.textSize:.3f}cm" + return f"{emVal*2.54/72*self._textSize:.3f}cm" ## # Style Elements @@ -747,7 +747,7 @@ class ToOdt(Tokenizer): etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr) theAttr = {} - theAttr[_mkTag("style", "font-name")] = self.textFont + theAttr[_mkTag("style", "font-name")] = self._textFont theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-size")] = self._fSizeText theAttr[_mkTag("fo", "language")] = self._dLanguage @@ -764,7 +764,7 @@ class ToOdt(Tokenizer): xStyl = etree.SubElement(self._xStyl, _mkTag("style", "style"), attrib=theAttr) theAttr = {} - theAttr[_mkTag("style", "font-name")] = self.textFont + theAttr[_mkTag("style", "font-name")] = self._textFont theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-size")] = self._fSizeText etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr) @@ -787,7 +787,7 @@ class ToOdt(Tokenizer): etree.SubElement(xStyl, _mkTag("style", "paragraph-properties"), attrib=theAttr) theAttr = {} - theAttr[_mkTag("style", "font-name")] = self.textFont + theAttr[_mkTag("style", "font-name")] = self._textFont theAttr[_mkTag("fo", "font-family")] = self._fontFamily theAttr[_mkTag("fo", "font-size")] = self._fSizeHead etree.SubElement(xStyl, _mkTag("style", "text-properties"), attrib=theAttr) @@ -816,8 +816,8 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopText) oStyle.setMarginBottom(self._mBotText) - oStyle.setLineHeight(self._lineHeight) - oStyle.setFontName(self.textFont) + oStyle.setLineHeight(self._fLineHeight) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeText) oStyle.setTextAlign(self._textAlign) @@ -834,8 +834,8 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopMeta) oStyle.setMarginBottom(self._mBotMeta) - oStyle.setLineHeight(self._lineHeight) - oStyle.setFontName(self.textFont) + oStyle.setLineHeight(self._fLineHeight) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeText) oStyle.setColor(self._colMetaTx) @@ -855,7 +855,7 @@ class ToOdt(Tokenizer): oStyle.setTextAlign("center") oStyle.setMarginTop(self._mTopTitle) oStyle.setMarginBottom(self._mBotTitle) - oStyle.setFontName(self.textFont) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeTitle) oStyle.setFontWeight("bold") @@ -874,7 +874,7 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopHead1) oStyle.setMarginBottom(self._mBotHead1) - oStyle.setFontName(self.textFont) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeHead1) oStyle.setColor(self._colHead12) @@ -895,7 +895,7 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopHead2) oStyle.setMarginBottom(self._mBotHead2) - oStyle.setFontName(self.textFont) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeHead2) oStyle.setColor(self._colHead12) @@ -916,7 +916,7 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopHead3) oStyle.setMarginBottom(self._mBotHead3) - oStyle.setFontName(self.textFont) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeHead3) oStyle.setColor(self._colHead34) @@ -937,7 +937,7 @@ class ToOdt(Tokenizer): oStyle.setClass("text") oStyle.setMarginTop(self._mTopHead4) oStyle.setMarginBottom(self._mBotHead4) - oStyle.setFontName(self.textFont) + oStyle.setFontName(self._textFont) oStyle.setFontFamily(self._fontFamily) oStyle.setFontSize(self._fSizeHead4) oStyle.setColor(self._colHead34) @@ -972,7 +972,7 @@ class ToOdt(Tokenizer): xPar = etree.SubElement(xHead, _mkTag("text", "p"), attrib={ _mkTag("text", "style-name"): "Header" }) - xPar.text = self.headerText.strip() + " " + xPar.text = self._headerText.strip() + " " xTail = etree.SubElement(xPar, _mkTag("text", "page-number"), attrib={ _mkTag("text", "select-page"): "current" diff --git a/novelwriter/error.py b/novelwriter/error.py index bde60599..ea94ac92 100644 --- a/novelwriter/error.py +++ b/novelwriter/error.py @@ -120,27 +120,29 @@ class NWErrorMessage(QDialog): kernelVersion = "Unknown" try: + import lxml + lxmlVersion = lxml.__version__ + except Exception: + lxmlVersion = "Unknown" + + try: + import enchant + enchantVersion = enchant.__version__ + except Exception: + enchantVersion = "Unknown" + + try: + exTrace = "\n".join(format_tb(exTrace)) self.msgBody.setPlainText(( "Environment:\n" - "novelWriter Version: {nwVersion}\n" - "Host OS: {osType} ({osKernel})\n" - "Python: {pyVersion} ({pyHexVer:#x})\n" - "Qt: {qtVers}, PyQt: {pyqtVers}\n" - "\n" - "{exType}:\n{exMessage}\n" - "\n" - "Traceback:\n{exTrace}\n" - ).format( - nwVersion=__version__, - osType=sys.platform, - osKernel=kernelVersion, - pyVersion=sys.version.split()[0], - pyHexVer=sys.hexversion, - qtVers=QT_VERSION_STR, - pyqtVers=PYQT_VERSION_STR, - exType=exType.__name__, - exMessage=str(exValue), - exTrace="\n".join(format_tb(exTrace)), + f"novelWriter Version: {__version__}\n" + f"Host OS: {sys.platform} ({kernelVersion})\n" + f"Python: {sys.version.split()[0]} ({sys.hexversion:#x})\n" + f"Qt: {QT_VERSION_STR}, PyQt: {PYQT_VERSION_STR}\n" + f"lxml: {lxmlVersion}\n" + f"enchant: {enchantVersion}\n\n" + f"{exType.__name__}:\n{str(exValue)}\n\n" + f"Traceback:\n{exTrace}\n" )) except Exception: self.msgBody.setPlainText("Failed to generate error report ...") diff --git a/tests/test_core/test_core_document.py b/tests/test_core/test_core_document.py index 16a6ab0f..0559fb6d 100644 --- a/tests/test_core/test_core_document.py +++ b/tests/test_core/test_core_document.py @@ -44,6 +44,7 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal): # Not a valid handle theDoc = NWDoc(theProject, "stuff") + assert bool(theDoc) is False assert theDoc.readDocument() is None # Non-existent handle @@ -67,6 +68,8 @@ def testCoreDocument_LoadSave(monkeypatch, mockGUI, nwMinimal): assert nHandle is not None xHandle = theProject.newFile("New File", nwItemClass.NOVEL, nHandle) theDoc = NWDoc(theProject, xHandle) + assert bool(theDoc) is True + assert repr(theDoc) == f"" assert theDoc.readDocument() == "" # Write Document diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index a7ea82e5..ed6426b1 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -219,6 +219,9 @@ def testCoreIndex_CheckThese(nwMinimal, mockGUI): assert theIndex.notesChangedSince(0) is True assert theIndex.indexChangedSince(0) is True + assert theIndex.getHandleHeaderLevel(cHandle) == "H1" + assert theIndex.getHandleHeaderLevel(nHandle) == "H1" + # Zero Items assert theIndex.checkThese([], cItem) == [] diff --git a/tests/test_core/test_core_project.py b/tests/test_core/test_core_project.py index 35e3ca2d..e9ef8c3c 100644 --- a/tests/test_core/test_core_project.py +++ b/tests/test_core/test_core_project.py @@ -48,36 +48,39 @@ def testCoreProject_NewMinimal(fncDir, outDir, refDir, mockGUI): theProject.projTree.setSeed(42) # Setting no data should fail - assert not theProject.newProject({}) + assert theProject.newProject({}) is False + + # Wrong type should also fail + assert theProject.newProject("stuff") is False # Try again with a proper path - assert theProject.newProject({"projPath": fncDir}) - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.newProject({"projPath": fncDir}) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True # Creating the project once more should fail - assert not theProject.newProject({"projPath": fncDir}) + assert theProject.newProject({"projPath": fncDir}) is False # Check the new project copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) # Open again - assert theProject.openProject(projFile) + assert theProject.openProject(projFile) is True # Save and close - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) - assert not theProject.projChanged + assert theProject.projChanged is False # Open a second time - assert theProject.openProject(projFile) - assert not theProject.openProject(projFile) - assert theProject.openProject(projFile, overrideLock=True) - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.openProject(projFile) is True + assert theProject.openProject(projFile) is False + assert theProject.openProject(projFile, overrideLock=True) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) @@ -116,9 +119,9 @@ def testCoreProject_NewCustomA(fncDir, outDir, refDir, mockGUI): theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.newProject(projData) - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.newProject(projData) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) @@ -158,9 +161,9 @@ def testCoreProject_NewCustomB(fncDir, outDir, refDir, mockGUI): theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.newProject(projData) - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.newProject(projData) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) @@ -207,11 +210,11 @@ def testCoreProject_NewSampleA(fncDir, tmpConf, mockGUI, tmpDir): srcDoc = os.path.join(srcSample, "content", docFile) zipObj.write(srcDoc, "content/"+docFile) - assert theProject.newProject(projData) - assert theProject.openProject(fncDir) + assert theProject.newProject(projData) is True + assert theProject.openProject(fncDir) is True assert theProject.projName == "Sample Project" - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.saveProject() is True + assert theProject.closeProject() is True os.unlink(dstSample) # END Test testCoreProject_NewSampleA @@ -242,11 +245,11 @@ def testCoreProject_NewSampleB(monkeypatch, fncDir, tmpConf, mockGUI, tmpDir): assert not theProject.newProject(projData) monkeypatch.setattr(nwFiles, "PROJ_FILE", "nwProject.nwx") - assert theProject.newProject(projData) - assert theProject.openProject(fncDir) + assert theProject.newProject(projData) is True + assert theProject.openProject(fncDir) is True assert theProject.projName == "Sample Project" - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.saveProject() is True + assert theProject.closeProject() is True # Misdirect the appRoot path so neither is possible tmpConf.appRoot = tmpDir @@ -266,11 +269,11 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI): theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.newProject({"projPath": fncDir}) - assert theProject.setProjectPath(fncDir) - assert theProject.saveProject() - assert theProject.closeProject() - assert theProject.openProject(projFile) + assert theProject.newProject({"projPath": fncDir}) is True + assert theProject.setProjectPath(fncDir) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True + assert theProject.openProject(projFile) is True assert isinstance(theProject.newRoot("Novel", nwItemClass.NOVEL), type(None)) assert isinstance(theProject.newRoot("Plot", nwItemClass.PLOT), type(None)) @@ -281,13 +284,13 @@ def testCoreProject_NewRoot(fncDir, outDir, refDir, mockGUI): assert isinstance(theProject.newRoot("Custom1", nwItemClass.CUSTOM), str) assert isinstance(theProject.newRoot("Custom2", nwItemClass.CUSTOM), str) - assert theProject.projChanged - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.projChanged is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) - assert not theProject.projChanged + assert theProject.projChanged is False # END Test testCoreProject_NewRoot @@ -303,21 +306,21 @@ def testCoreProject_NewFile(fncDir, outDir, refDir, mockGUI): theProject = NWProject(mockGUI) theProject.projTree.setSeed(42) - assert theProject.newProject({"projPath": fncDir}) - assert theProject.setProjectPath(fncDir) - assert theProject.saveProject() - assert theProject.closeProject() - assert theProject.openProject(projFile) + assert theProject.newProject({"projPath": fncDir}) is True + assert theProject.setProjectPath(fncDir) is True + assert theProject.saveProject() is True + assert theProject.closeProject() is True + assert theProject.openProject(projFile) is True assert isinstance(theProject.newFile("Hello", nwItemClass.NOVEL, "31489056e0916"), str) assert isinstance(theProject.newFile("Jane", nwItemClass.CHARACTER, "71ee45a3c0db9"), str) assert theProject.projChanged - assert theProject.saveProject() - assert theProject.closeProject() + assert theProject.saveProject() is True + assert theProject.closeProject() is True copyfile(projFile, testFile) assert cmpFiles(testFile, compFile, [2, 6, 7, 8]) - assert not theProject.projChanged + assert theProject.projChanged is False # END Test testCoreProject_NewFile @@ -466,6 +469,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir): """ theProject = NWProject(mockGUI) testFile = os.path.join(nwMinimal, "nwProject.nwx") + backFile = os.path.join(nwMinimal, "nwProject.bak") compFile = os.path.join(refDir, os.path.pardir, "minimal", "nwProject.nwx") # Nothing to save @@ -476,7 +480,7 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir): # Fail on folder structure check with monkeypatch.context() as mp: - mp.setattr("os.path.isdir", lambda *args: False) + mp.setattr("os.path.isdir", lambda *a: False) assert theProject.saveProject() is False # Fail on open file @@ -484,6 +488,12 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir): mp.setattr("builtins.open", causeOSError) assert theProject.saveProject() is False + # Fail on creating .bak file + with monkeypatch.context() as mp: + mp.setattr("os.replace", causeOSError) + assert theProject.saveProject() is False + assert os.path.isfile(backFile) is False + # Successful save saveCount = theProject.saveCount autoCount = theProject.autoCount @@ -492,6 +502,9 @@ def testCoreProject_Save(monkeypatch, nwMinimal, mockGUI, refDir): assert theProject.autoCount == autoCount assert cmpFiles(testFile, compFile, [2, 6, 7, 8, 9]) + # Check that a second save creates a .bak file + assert os.path.isfile(backFile) is True + # Successful autosave saveCount = theProject.saveCount autoCount = theProject.autoCount diff --git a/tests/test_core/test_core_tohtml.py b/tests/test_core/test_core_tohtml.py index f8a852d4..f93fdeba 100644 --- a/tests/test_core/test_core_tohtml.py +++ b/tests/test_core/test_core_tohtml.py @@ -38,12 +38,12 @@ def testCoreToHtml_ConvertFormat(mockGUI): # Novel Files Headers # =================== - theHtml.isNovel = True - theHtml.isNote = False - theHtml.isFirst = True + theHtml._isNovel = True + theHtml._isNote = False + theHtml._isFirst = True # Header 1 - theHtml.theText = "# Partition\n" + theHtml._theText = "# Partition\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -51,7 +51,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Header 2 - theHtml.theText = "## Chapter Title\n" + theHtml._theText = "## Chapter Title\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -59,19 +59,19 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Header 3 - theHtml.theText = "### Scene Title\n" + theHtml._theText = "### Scene Title\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Scene Title

\n" # Header 4 - theHtml.theText = "#### Section Title\n" + theHtml._theText = "#### Section Title\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Section Title

\n" # Title - theHtml.theText = "#! Title\n" + theHtml._theText = "#! Title\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -79,7 +79,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Unnumbered - theHtml.theText = "##! Prologue\n" + theHtml._theText = "##! Prologue\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Prologue

\n" @@ -87,37 +87,37 @@ def testCoreToHtml_ConvertFormat(mockGUI): # Note Files Headers # ================== - theHtml.isNovel = False - theHtml.isNote = True - theHtml.isFirst = True + theHtml._isNovel = False + theHtml._isNote = True + theHtml._isFirst = True theHtml.setLinkHeaders(True) # Header 1 - theHtml.theText = "# Heading One\n" + theHtml._theText = "# Heading One\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Heading One

\n" # Header 2 - theHtml.theText = "## Heading Two\n" + theHtml._theText = "## Heading Two\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Heading Two

\n" # Header 3 - theHtml.theText = "### Heading Three\n" + theHtml._theText = "### Heading Three\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Heading Three

\n" # Header 4 - theHtml.theText = "#### Heading Four\n" + theHtml._theText = "#### Heading Four\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Heading Four

\n" # Title - theHtml.theText = "#! Heading One\n" + theHtml._theText = "#! Heading One\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -125,7 +125,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Unnumbered - theHtml.theText = "##! Heading Two\n" + theHtml._theText = "##! Heading Two\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "

Heading Two

\n" @@ -134,7 +134,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): # ========== # Text - theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" + theHtml._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -143,7 +143,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Text w/Hard Break - theHtml.theText = "Line one \nLine two \nLine three\n" + theHtml._theText = "Line one \nLine two \nLine three\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -151,13 +151,13 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Synopsis - theHtml.theText = "%synopsis: The synopsis ...\n" + theHtml._theText = "%synopsis: The synopsis ...\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "" theHtml.setSynopsis(True) - theHtml.theText = "%synopsis: The synopsis ...\n" + theHtml._theText = "%synopsis: The synopsis ...\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -165,13 +165,13 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Comment - theHtml.theText = "% A comment ...\n" + theHtml._theText = "% A comment ...\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "" theHtml.setComments(True) - theHtml.theText = "% A comment ...\n" + theHtml._theText = "% A comment ...\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -179,13 +179,13 @@ def testCoreToHtml_ConvertFormat(mockGUI): ) # Keywords - theHtml.theText = "@char: Bod, Jane\n" + theHtml._theText = "@char: Bod, Jane\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == "" theHtml.setKeywords(True) - theHtml.theText = "@char: Bod, Jane\n" + theHtml._theText = "@char: Bod, Jane\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -195,7 +195,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): # Multiple Keywords theHtml.setKeywords(True) - theHtml.theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" + theHtml._theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -218,7 +218,7 @@ def testCoreToHtml_ConvertFormat(mockGUI): theHtml.setPreview(True, True) # Text (HTML4) - theHtml.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" + theHtml._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -238,15 +238,15 @@ def testCoreToHtml_ConvertDirect(mockGUI): mockGUI.theIndex = NWIndex(theProject) theHtml = ToHtml(theProject) - theHtml.isNovel = True - theHtml.isNote = False + theHtml._isNovel = True + theHtml._isNote = False theHtml.setLinkHeaders(True) # Special Titles # ============== # Title - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_TITLE, 1, "A Title", None, theHtml.A_PBB | theHtml.A_CENTRE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), ] @@ -257,7 +257,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): ) # Unnumbered - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_UNNUM, 1, "Prologue", None, theHtml.A_PBB), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), ] @@ -271,7 +271,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): # ========== # Separator - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_SEP, 1, "* * *", None, theHtml.A_CENTRE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), ] @@ -279,7 +279,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): assert theHtml.theResult == "

* * *

\n" # Skip - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_SKIP, 1, "", None, theHtml.A_NONE), (theHtml.T_EMPTY, 1, "", None, theHtml.A_NONE), ] @@ -293,7 +293,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): # Align Left theHtml.setStyles(False) - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT), ] theHtml.doConvert() @@ -304,7 +304,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): theHtml.setStyles(True) # Align Left - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_LEFT), ] theHtml.doConvert() @@ -313,7 +313,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): ) # Align Right - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_RIGHT), ] theHtml.doConvert() @@ -322,7 +322,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): ) # Align Centre - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_CENTRE), ] theHtml.doConvert() @@ -331,7 +331,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): ) # Align Justify - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_JUSTIFY), ] theHtml.doConvert() @@ -343,7 +343,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): # ========== # Page Break Always - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_HEAD1, 1, "A Title", None, theHtml.A_PBB | theHtml.A_PBA), ] theHtml.doConvert() @@ -356,7 +356,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): # ====== # Indent Left - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_L), (theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE), ] @@ -366,7 +366,7 @@ def testCoreToHtml_ConvertDirect(mockGUI): ) # Indent Right - theHtml.theTokens = [ + theHtml._theTokens = [ (theHtml.T_TEXT, 1, "Some text ...", [], theHtml.A_IND_R), (theHtml.T_EMPTY, 2, "", None, theHtml.A_NONE), ] @@ -384,33 +384,33 @@ def testCoreToHtml_SpecialCases(mockGUI): """ theProject = NWProject(mockGUI) theHtml = ToHtml(theProject) - theHtml.isNovel = True + theHtml._isNovel = True # Greater/Lesser than symbols # =========================== - theHtml.theText = "Text with > and < with some **bold text** in it.\n" + theHtml._theText = "Text with > and < with some **bold text** in it.\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( "

Text with > and < with some bold text in it.

\n" ) - theHtml.theText = "Text with some <**bold text**> in it.\n" + theHtml._theText = "Text with some <**bold text**> in it.\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( "

Text with some <bold text> in it.

\n" ) - theHtml.theText = "Let's > be > _difficult **shall** > we_?\n" + theHtml._theText = "Let's > be > _difficult **shall** > we_?\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( "

Let's > be > difficult shall > we?

\n" ) - theHtml.theText = "Test > text _<**bold**>_ and more.\n" + theHtml._theText = "Test > text _<**bold**>_ and more.\n" theHtml.tokenizeText() theHtml.doConvert() assert theHtml.theResult == ( @@ -426,7 +426,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir): """ theProject = NWProject(mockGUI) theHtml = ToHtml(theProject) - theHtml.isNovel = True + theHtml._isNovel = True # Build Project # ============= @@ -472,7 +472,7 @@ def testCoreToHtml_Complex(mockGUI, fncDir): ] for i in range(len(docText)): - theHtml.theText = docText[i] + theHtml._theText = docText[i] theHtml.doPreProcessing() theHtml.tokenizeText() theHtml.doConvert() @@ -526,7 +526,7 @@ def testCoreToHtml_Methods(mockGUI): # Auto-Replace, keep Unicode docText = "Text with & short–dash, long—dash …\n" - theHtml.theText = docText + theHtml._theText = docText theHtml.setReplaceUnicode(False) theHtml.doPreProcessing() theHtml.tokenizeText() @@ -537,7 +537,7 @@ def testCoreToHtml_Methods(mockGUI): # Auto-Replace, replace Unicode docText = "Text with & short–dash, long—dash …\n" - theHtml.theText = docText + theHtml._theText = docText theHtml.setReplaceUnicode(True) theHtml.doPreProcessing() theHtml.tokenizeText() @@ -548,7 +548,7 @@ def testCoreToHtml_Methods(mockGUI): # With Preview theHtml.setPreview(True, True) - theHtml.theText = docText + theHtml._theText = docText theHtml.doPreProcessing() theHtml.tokenizeText() theHtml.doConvert() diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index ce36d96b..421ab6ee 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -36,31 +36,31 @@ def testCoreToken_Setters(mockGUI): theToken = Tokenizer(theProject) # Verify defaults - assert theToken.fmtTitle == "%title%" - assert theToken.fmtChapter == "%title%" - assert theToken.fmtUnNum == "%title%" - assert theToken.fmtScene == "%title%" - assert theToken.fmtSection == "%title%" - assert theToken.textFont == "Serif" - assert theToken.textSize == 11 - assert theToken.textFixed is False - assert theToken.lineHeight == 1.15 - assert theToken.blockIndent == 4.0 - assert theToken.doJustify is False - assert theToken.marginTitle == (1.000, 0.500) - assert theToken.marginHead1 == (1.000, 0.500) - assert theToken.marginHead2 == (0.834, 0.500) - assert theToken.marginHead3 == (0.584, 0.500) - assert theToken.marginHead4 == (0.584, 0.500) - assert theToken.marginText == (0.000, 0.584) - assert theToken.marginMeta == (0.000, 0.584) - assert theToken.hideScene is False - assert theToken.hideSection is False - assert theToken.linkHeaders is False - assert theToken.doBodyText is True - assert theToken.doSynopsis is False - assert theToken.doComments is False - assert theToken.doKeywords is False + assert theToken._fmtTitle == "%title%" + assert theToken._fmtChapter == "%title%" + assert theToken._fmtUnNum == "%title%" + assert theToken._fmtScene == "%title%" + assert theToken._fmtSection == "%title%" + assert theToken._textFont == "Serif" + assert theToken._textSize == 11 + assert theToken._textFixed is False + assert theToken._lineHeight == 1.15 + assert theToken._blockIndent == 4.0 + assert theToken._doJustify is False + assert theToken._marginTitle == (1.000, 0.500) + assert theToken._marginHead1 == (1.000, 0.500) + assert theToken._marginHead2 == (0.834, 0.500) + assert theToken._marginHead3 == (0.584, 0.500) + assert theToken._marginHead4 == (0.584, 0.500) + assert theToken._marginText == (0.000, 0.584) + assert theToken._marginMeta == (0.000, 0.584) + assert theToken._hideScene is False + assert theToken._hideSection is False + assert theToken._linkHeaders is False + assert theToken._doBodyText is True + assert theToken._doSynopsis is False + assert theToken._doComments is False + assert theToken._doKeywords is False # Set new values theToken.setTitleFormat("T: %title%") @@ -86,42 +86,42 @@ def testCoreToken_Setters(mockGUI): theToken.setKeywords(True) # Check new values - assert theToken.fmtTitle == "T: %title%" - assert theToken.fmtChapter == "C: %title%" - assert theToken.fmtUnNum == "U: %title%" - assert theToken.fmtScene == "S: %title%" - assert theToken.fmtSection == "X: %title%" - assert theToken.textFont == "Monospace" - assert theToken.textSize == 10 - assert theToken.textFixed is True - assert theToken.lineHeight == 2.0 - assert theToken.blockIndent == 6.0 - assert theToken.doJustify is True - assert theToken.marginTitle == (2.0, 2.0) - assert theToken.marginHead1 == (2.0, 2.0) - assert theToken.marginHead2 == (2.0, 2.0) - assert theToken.marginHead3 == (2.0, 2.0) - assert theToken.marginHead4 == (2.0, 2.0) - assert theToken.marginText == (2.0, 2.0) - assert theToken.marginMeta == (2.0, 2.0) - assert theToken.hideScene is True - assert theToken.hideSection is True - assert theToken.linkHeaders is True - assert theToken.doBodyText is False - assert theToken.doSynopsis is True - assert theToken.doComments is True - assert theToken.doKeywords is True + assert theToken._fmtTitle == "T: %title%" + assert theToken._fmtChapter == "C: %title%" + assert theToken._fmtUnNum == "U: %title%" + assert theToken._fmtScene == "S: %title%" + assert theToken._fmtSection == "X: %title%" + assert theToken._textFont == "Monospace" + assert theToken._textSize == 10 + assert theToken._textFixed is True + assert theToken._lineHeight == 2.0 + assert theToken._blockIndent == 6.0 + assert theToken._doJustify is True + assert theToken._marginTitle == (2.0, 2.0) + assert theToken._marginHead1 == (2.0, 2.0) + assert theToken._marginHead2 == (2.0, 2.0) + assert theToken._marginHead3 == (2.0, 2.0) + assert theToken._marginHead4 == (2.0, 2.0) + assert theToken._marginText == (2.0, 2.0) + assert theToken._marginMeta == (2.0, 2.0) + assert theToken._hideScene is True + assert theToken._hideSection is True + assert theToken._linkHeaders is True + assert theToken._doBodyText is False + assert theToken._doSynopsis is True + assert theToken._doComments is True + assert theToken._doKeywords is True # Check Limits theToken.setLineHeight(0.0) - assert theToken.lineHeight == 0.5 + assert theToken._lineHeight == 0.5 theToken.setLineHeight(10.0) - assert theToken.lineHeight == 5.0 + assert theToken._lineHeight == 5.0 theToken.setBlockIndent(-6.0) - assert theToken.blockIndent == 0.0 + assert theToken._blockIndent == 0.0 theToken.setBlockIndent(60.0) - assert theToken.blockIndent == 10.0 + assert theToken._blockIndent == 10.0 # END Test testCoreToken_Setters @@ -166,43 +166,43 @@ def testCoreToken_TextOps(monkeypatch, nwMinimal, mockGUI): # First Page assert theToken.addRootHeading("7695ce551d265") is True assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n" - assert theToken.theTokens[-1] == ( + assert theToken._theTokens[-1] == ( Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE ) # Not First Page assert theToken.addRootHeading("7695ce551d265") is True assert theToken.theMarkdown[-1] == "# Notes: Plot\n\n" - assert theToken.theTokens[-1] == ( + assert theToken._theTokens[-1] == ( Tokenizer.T_TITLE, 0, "Notes: Plot", None, Tokenizer.A_CENTRE | Tokenizer.A_PBB ) # Set Text assert theToken.setText("stuff") is False assert theToken.setText(sHandle) is True - assert theToken.theText == docText + assert theToken._theText == docText with monkeypatch.context() as mp: mp.setattr("novelwriter.constants.nwConst.MAX_DOCSIZE", 100) assert theToken.setText(sHandle, docText) is True - assert theToken.theText == ( + assert theToken._theText == ( "# ERROR\n\n" "Document 'New Scene' is too big (0.00 MB). Skipping.\n\n" ) assert theToken.setText(sHandle, docText) is True - assert theToken.theText == docText + assert theToken._theText == docText - assert theToken.isNone is False - assert theToken.isNovel is True - assert theToken.isNote is False + assert theToken._isNone is False + assert theToken._isNovel is True + assert theToken._isNote is False # Pre Processing theToken.doPreProcessing() - assert theToken.theText == docTextR + assert theToken._theText == docTextR # Post Processing - theToken.theResult = r"This is text with escapes: \** \~~ \__" + theToken._theResult = r"This is text with escapes: \** \~~ \__" theToken.doPostProcessing() assert theToken.theResult == "This is text with escapes: ** ~~ __" @@ -229,26 +229,26 @@ def testCoreToken_HeaderFormat(mockGUI): # ===== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.isFirst = True - theToken.theText = "#! Novel Title\n" + theToken._isNovel = True + theToken._isNote = False + theToken._isFirst = True + theToken._theText = "#! Novel Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_TITLE, 1, "Novel Title", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "#! Novel Title\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.isFirst = True - theToken.theText = "#! Note Title\n" + theToken._isNovel = False + theToken._isNote = True + theToken._isFirst = True + theToken._theText = "#! Note Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -258,26 +258,26 @@ def testCoreToken_HeaderFormat(mockGUI): # ======== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.isFirst = True - theToken.theText = "# Novel Title\n" + theToken._isNovel = True + theToken._isNote = False + theToken._isFirst = True + theToken._theText = "# Novel Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Novel Title", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "# Novel Title\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.isFirst = True - theToken.theText = "# Note Title\n" + theToken._isNovel = False + theToken._isNote = True + theToken._isFirst = True + theToken._theText = "# Note Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Note Title", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -287,24 +287,24 @@ def testCoreToken_HeaderFormat(mockGUI): # ======== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.theText = "## Chapter One\n" + theToken._isNovel = True + theToken._isNote = False + theToken._theText = "## Chapter One\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "## Chapter One\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.theText = "## Heading 2\n" + theToken._isNovel = False + theToken._isNote = True + theToken._theText = "## Heading 2\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Heading 2", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -314,24 +314,24 @@ def testCoreToken_HeaderFormat(mockGUI): # ======== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.theText = "### Scene One\n" + theToken._isNovel = True + theToken._isNote = False + theToken._theText = "### Scene One\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD3, 1, "Scene One", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "### Scene One\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.theText = "### Heading 3\n" + theToken._isNovel = False + theToken._isNote = True + theToken._theText = "### Heading 3\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD3, 1, "Heading 3", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -341,24 +341,24 @@ def testCoreToken_HeaderFormat(mockGUI): # ======== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.theText = "#### A Section\n" + theToken._isNovel = True + theToken._isNote = False + theToken._theText = "#### A Section\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD4, 1, "A Section", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "#### A Section\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.theText = "#### Heading 4\n" + theToken._isNovel = False + theToken._isNote = True + theToken._theText = "#### Heading 4\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD4, 1, "Heading 4", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -368,24 +368,24 @@ def testCoreToken_HeaderFormat(mockGUI): # ===== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.theText = "#! Title\n" + theToken._isNovel = True + theToken._isNote = False + theToken._theText = "#! Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_TITLE, 1, "Title", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "#! Title\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.theText = "#! Title\n" + theToken._isNovel = False + theToken._isNote = True + theToken._theText = "#! Title\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -395,24 +395,24 @@ def testCoreToken_HeaderFormat(mockGUI): # ========== # Story File - theToken.isNovel = True - theToken.isNote = False - theToken.theText = "##! Prologue\n" + theToken._isNovel = True + theToken._isNote = False + theToken._theText = "##! Prologue\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_UNNUM, 1, "Prologue", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] assert theToken.theMarkdown[-1] == "##! Prologue\n\n" # Note File - theToken.isNovel = False - theToken.isNote = True - theToken.theText = "##! Prologue\n" + theToken._isNovel = False + theToken._isNote = True + theToken._theText = "##! Prologue\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Prologue", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -430,9 +430,9 @@ def testCoreToken_MetaFormat(mockGUI): theToken.setKeepMarkdown(True) # Comment - theToken.theText = "% A comment\n" + theToken._theText = "% A comment\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_COMMENT, 1, "A comment", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -443,15 +443,15 @@ def testCoreToken_MetaFormat(mockGUI): assert theToken.theMarkdown[-1] == "% A comment\n\n" # Symopsis - theToken.theText = "%synopsis: The synopsis\n" + theToken._theText = "%synopsis: The synopsis\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] - theToken.theText = "% synopsis: The synopsis\n" + theToken._theText = "% synopsis: The synopsis\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SYNOPSIS, 1, "The synopsis", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -462,9 +462,9 @@ def testCoreToken_MetaFormat(mockGUI): assert theToken.theMarkdown[-1] == "% synopsis: The synopsis\n\n" # Keyword - theToken.theText = "@char: Bod\n" + theToken._theText = "@char: Bod\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_KEYWORD, 1, "char: Bod", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -474,12 +474,12 @@ def testCoreToken_MetaFormat(mockGUI): theToken.tokenizeText() assert theToken.theMarkdown[-1] == "@char: Bod\n\n" - theToken.theText = "@pov: Bod\n@plot: Main\n@location: Europe\n" + theToken._theText = "@pov: Bod\n@plot: Main\n@location: Europe\n" theToken.tokenizeText() styTop = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG styMid = Tokenizer.A_NONE | Tokenizer.A_Z_BTMMRG | Tokenizer.A_Z_TOPMRG styBtm = Tokenizer.A_NONE | Tokenizer.A_Z_TOPMRG - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_KEYWORD, 1, "pov: Bod", None, styTop), (Tokenizer.T_KEYWORD, 2, "plot: Main", None, styMid), (Tokenizer.T_KEYWORD, 3, "location: Europe", None, styBtm), @@ -501,7 +501,7 @@ def testCoreToken_MarginFormat(mockGUI): # Alignment and Indentation dblIndent = Tokenizer.A_IND_L | Tokenizer.A_IND_R rIndAlign = Tokenizer.A_RIGHT | Tokenizer.A_IND_R - theToken.theText = ( + theToken._theText = ( "Some regular text\n\n" "Some left-aligned text <<\n\n" ">> Some right-aligned text\n\n" @@ -512,7 +512,7 @@ def testCoreToken_MarginFormat(mockGUI): ">> Right-indent, right-aligned <\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_TEXT, 1, "Some regular text", [], Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_TEXT, 3, "Some left-aligned text", [], Tokenizer.A_LEFT), @@ -554,9 +554,9 @@ def testCoreToken_TextFormat(mockGUI): theToken.setKeepMarkdown(True) # Text - theToken.theText = "Some plain text\non two lines\n\n\n" + theToken._theText = "Some plain text\non two lines\n\n\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_TEXT, 1, "Some plain text", [], Tokenizer.A_NONE), (Tokenizer.T_TEXT, 2, "on two lines", [], Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE), @@ -567,7 +567,7 @@ def testCoreToken_TextFormat(mockGUI): theToken.setBodyText(False) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_EMPTY, 3, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -576,9 +576,9 @@ def testCoreToken_TextFormat(mockGUI): theToken.setBodyText(True) # Text Emphasis - theToken.theText = "Some **bolded text** on this lines\n" + theToken._theText = "Some **bolded text** on this lines\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ ( Tokenizer.T_TEXT, 1, "Some **bolded text** on this lines", @@ -592,9 +592,9 @@ def testCoreToken_TextFormat(mockGUI): ] assert theToken.theMarkdown[-1] == "Some **bolded text** on this lines\n\n" - theToken.theText = "Some _italic text_ on this lines\n" + theToken._theText = "Some _italic text_ on this lines\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ ( Tokenizer.T_TEXT, 1, "Some _italic text_ on this lines", @@ -608,9 +608,9 @@ def testCoreToken_TextFormat(mockGUI): ] assert theToken.theMarkdown[-1] == "Some _italic text_ on this lines\n\n" - theToken.theText = "Some **_bold italic text_** on this lines\n" + theToken._theText = "Some **_bold italic text_** on this lines\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ ( Tokenizer.T_TEXT, 1, "Some **_bold italic text_** on this lines", @@ -626,9 +626,9 @@ def testCoreToken_TextFormat(mockGUI): ] assert theToken.theMarkdown[-1] == "Some **_bold italic text_** on this lines\n\n" - theToken.theText = "Some ~~strikethrough text~~ on this lines\n" + theToken._theText = "Some ~~strikethrough text~~ on this lines\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ ( Tokenizer.T_TEXT, 1, "Some ~~strikethrough text~~ on this lines", @@ -642,9 +642,9 @@ def testCoreToken_TextFormat(mockGUI): ] assert theToken.theMarkdown[-1] == "Some ~~strikethrough text~~ on this lines\n\n" - theToken.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" + theToken._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ ( Tokenizer.T_TEXT, 1, "Some **nested bold and _italic_ and ~~strikethrough~~ text** here", @@ -674,7 +674,7 @@ def testCoreToken_SpecialFormat(mockGUI): theProject = NWProject(mockGUI) theToken = Tokenizer(theProject) - theToken.isNovel = True + theToken._isNovel = True # New Page # ======== @@ -689,45 +689,45 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Command wo/Space - theToken.isFirst = True - theToken.theText = ( + theToken._isFirst = True + theToken._theText = ( "# Title One\n\n" "[NEWPAGE]\n\n" "# Title Two\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == correctResp + assert theToken._theTokens == correctResp # Command w/Space - theToken.isFirst = True - theToken.theText = ( + theToken._isFirst = True + theToken._theText = ( "# Title One\n\n" "[NEW PAGE]\n\n" "# Title Two\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == correctResp + assert theToken._theTokens == correctResp # Trailing Spaces - theToken.isFirst = True - theToken.theText = ( + theToken._isFirst = True + theToken._theText = ( "# Title One\n\n" "[NEW PAGE] \t\n\n" "# Title Two\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == correctResp + assert theToken._theTokens == correctResp # Single Empty Paragraph # ====================== - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE] \n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE), @@ -741,13 +741,13 @@ def testCoreToken_SpecialFormat(mockGUI): # ========================= # One Skip - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE:1] \n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE), @@ -758,13 +758,13 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Three Skips - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE:3] \n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_SKIP, 3, "", None, Tokenizer.A_NONE), @@ -777,13 +777,13 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Malformed Command, Case 1 - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE:3xa] \n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -793,13 +793,13 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Malformed Command, Case 2 - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE:3.5]\n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -809,13 +809,13 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Malformed Command, Case 3 - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[VSPACE:-1]\n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -828,14 +828,14 @@ def testCoreToken_SpecialFormat(mockGUI): # ============================== # Single Skip - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[NEW PAGE]\n\n" "[VSPACE]\n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -847,14 +847,14 @@ def testCoreToken_SpecialFormat(mockGUI): ] # Multiple Skip - theToken.theText = ( + theToken._theText = ( "# Title One\n\n" "[NEW PAGE]\n\n" "[VSPACE:3]\n\n" "Some text to go here ...\n\n" ) theToken.tokenizeText() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "Title One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 2, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 4, "", None, Tokenizer.A_NONE), @@ -880,45 +880,45 @@ def testCoreToken_ProcessHeaders(mockGUI): theToken = Tokenizer(theProject) # Nothing - theToken.theText = "Some text ...\n" + theToken._theText = "Some text ...\n" assert theToken.doHeaders() is False - theToken.isNone = True + theToken._isNone = True assert theToken.doHeaders() is False - theToken.isNone = False + theToken._isNone = False assert theToken.doHeaders() is False - theToken.isNote = True + theToken._isNote = True assert theToken.doHeaders() is False - theToken.isNote = False + theToken._isNote = False ## # Story FIles ## - theToken.isNone = False - theToken.isNote = False - theToken.isNovel = True + theToken._isNone = False + theToken._isNote = False + theToken._isNovel = True # Titles # ====== # H1: Title, First Page - assert theToken.isFirst is True - theToken.theText = "# Part One\n" + assert theToken._isFirst is True + theToken._theText = "# Part One\n" theToken.setTitleFormat(r"T: %title%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H1: Title, Not First Page - assert theToken.isFirst is False - theToken.theText = "# Part One\n" + assert theToken._isFirst is False + theToken._theText = "# Part One\n" theToken.setTitleFormat(r"T: %title%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD1, 1, "T: Part One", None, Tokenizer.A_PBB | Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -927,52 +927,52 @@ def testCoreToken_ProcessHeaders(mockGUI): # ======== # H2: Chapter - theToken.theText = "## Chapter One\n" + theToken._theText = "## Chapter One\n" theToken.setChapterFormat(r"C: %title%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "C: Chapter One", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H2: Unnumbered Chapter - theToken.theText = "##! Prologue\n" + theToken._theText = "##! Prologue\n" theToken.setUnNumberedFormat(r"U: %title%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_UNNUM, 1, "U: Prologue", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H2: Chapter Word Number - theToken.theText = "## Chapter\n" + theToken._theText = "## Chapter\n" theToken.setChapterFormat(r"Chapter %chw%") - theToken.numChapter = 0 + theToken._numChapter = 0 theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Chapter One", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H2: Chapter Roman Number Upper Case - theToken.theText = "## Chapter\n" + theToken._theText = "## Chapter\n" theToken.setChapterFormat(r"Chapter %chI%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Chapter II", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H2: Chapter Roman Number Lower Case - theToken.theText = "## Chapter\n" + theToken._theText = "## Chapter\n" theToken.setChapterFormat(r"Chapter %chi%") theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD2, 1, "Chapter iii", None, Tokenizer.A_PBB), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -981,89 +981,89 @@ def testCoreToken_ProcessHeaders(mockGUI): # ====== # H3: Scene w/Title - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"S: %title%", False) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD3, 1, "S: Scene One", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene Hidden wo/Format - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"", True) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene wo/Format, first - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"", False) - theToken.firstScene = True + theToken._firstScene = True theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene wo/Format, not first - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"", False) - theToken.firstScene = False + theToken._firstScene = False theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene Separator, first - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"* * *", False) - theToken.firstScene = True + theToken._firstScene = True theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene Separator, not first - theToken.theText = "### Scene One\n" + theToken._theText = "### Scene One\n" theToken.setSceneFormat(r"* * *", False) - theToken.firstScene = False + theToken._firstScene = False theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene w/Absolute Number - theToken.theText = "### A Scene\n" + theToken._theText = "### A Scene\n" theToken.setSceneFormat(r"Scene %sca%", False) - theToken.numAbsScene = 0 - theToken.numChScene = 0 + theToken._numAbsScene = 0 + theToken._numChScene = 0 theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD3, 1, "Scene 1", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H3: Scene w/Chapter Number - theToken.theText = "### A Scene\n" + theToken._theText = "### A Scene\n" theToken.setSceneFormat(r"Scene %ch%.%sc%", False) - theToken.numAbsScene = 0 - theToken.numChScene = 1 + theToken._numAbsScene = 0 + theToken._numChScene = 1 theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD3, 1, "Scene 3.2", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] @@ -1072,52 +1072,51 @@ def testCoreToken_ProcessHeaders(mockGUI): # ======== # H4: Section Hidden wo/Format - theToken.theText = "#### A Section\n" + theToken._theText = "#### A Section\n" theToken.setSectionFormat(r"", True) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H4: Section Visible wo/Format - theToken.theText = "#### A Section\n" + theToken._theText = "#### A Section\n" theToken.setSectionFormat(r"", False) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SKIP, 1, "", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H4: Section w/Format - theToken.theText = "#### A Section\n" + theToken._theText = "#### A Section\n" theToken.setSectionFormat(r"X: %title%", False) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_HEAD4, 1, "X: A Section", None, Tokenizer.A_NONE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # H4: Section Separator - theToken.theText = "#### A Section\n" + theToken._theText = "#### A Section\n" theToken.setSectionFormat(r"* * *", False) theToken.tokenizeText() theToken.doHeaders() - assert theToken.theTokens == [ + assert theToken._theTokens == [ (Tokenizer.T_SEP, 1, "* * *", None, Tokenizer.A_CENTRE), (Tokenizer.T_EMPTY, 1, "", None, Tokenizer.A_NONE), ] # Check the first scene detector - assert theToken.firstScene is False - theToken.firstScene = True - assert theToken.firstScene is True - theToken.theText = "Some text ...\n" + assert theToken._firstScene is False + theToken._firstScene = True + theToken._theText = "Some text ...\n" theToken.tokenizeText() theToken.doHeaders() - assert theToken.firstScene is False + assert theToken._firstScene is False # END Test testCoreToken_ProcessHeaders diff --git a/tests/test_core/test_core_tomd.py b/tests/test_core/test_core_tomd.py index 267cce92..1c6f2371 100644 --- a/tests/test_core/test_core_tomd.py +++ b/tests/test_core/test_core_tomd.py @@ -38,42 +38,42 @@ def testCoreToMarkdown_ConvertFormat(mockGUI): # Headers # ======= - theMD.isNovel = True - theMD.isNote = False - theMD.isFirst = True + theMD._isNovel = True + theMD._isNote = False + theMD._isFirst = True # Header 1 - theMD.theText = "# Partition\n" + theMD._theText = "# Partition\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "# Partition\n\n" # Header 2 - theMD.theText = "## Chapter Title\n" + theMD._theText = "## Chapter Title\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "## Chapter Title\n\n" # Header 3 - theMD.theText = "### Scene Title\n" + theMD._theText = "### Scene Title\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "### Scene Title\n\n" # Header 4 - theMD.theText = "#### Section Title\n" + theMD._theText = "#### Section Title\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "#### Section Title\n\n" # Title - theMD.theText = "#! Title\n" + theMD._theText = "#! Title\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "# Title\n\n" # Unnumbered - theMD.theText = "##! Prologue\n" + theMD._theText = "##! Prologue\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "## Prologue\n\n" @@ -83,7 +83,7 @@ def testCoreToMarkdown_ConvertFormat(mockGUI): # Text for GitHub Markdown theMD.setGitHubMarkdown() - theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" + theMD._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == ( @@ -92,7 +92,7 @@ def testCoreToMarkdown_ConvertFormat(mockGUI): # Text for Standard Markdown theMD.setStandardMarkdown() - theMD.theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" + theMD._theText = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == ( @@ -100,50 +100,50 @@ def testCoreToMarkdown_ConvertFormat(mockGUI): ) # Text w/Hard Break - theMD.theText = "Line one \nLine two \nLine three\n" + theMD._theText = "Line one \nLine two \nLine three\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "Line one \nLine two \nLine three\n\n" # Synopsis - theMD.theText = "%synopsis: The synopsis ...\n" + theMD._theText = "%synopsis: The synopsis ...\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "" theMD.setSynopsis(True) - theMD.theText = "%synopsis: The synopsis ...\n" + theMD._theText = "%synopsis: The synopsis ...\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "**Synopsis:** The synopsis ...\n\n" # Comment - theMD.theText = "% A comment ...\n" + theMD._theText = "% A comment ...\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "" theMD.setComments(True) - theMD.theText = "% A comment ...\n" + theMD._theText = "% A comment ...\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "**Comment:** A comment ...\n\n" # Keywords - theMD.theText = "@char: Bod, Jane\n" + theMD._theText = "@char: Bod, Jane\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "" theMD.setKeywords(True) - theMD.theText = "@char: Bod, Jane\n" + theMD._theText = "@char: Bod, Jane\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == "**Characters:** Bod, Jane\n\n" # Multiple Keywords theMD.setKeywords(True) - theMD.theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" + theMD._theText = "## Chapter\n\n@pov: Bod\n@plot: Main\n@location: Europe\n\n" theMD.tokenizeText() theMD.doConvert() assert theMD.theResult == ( @@ -164,14 +164,14 @@ def testCoreToMarkdown_ConvertDirect(mockGUI): mockGUI.theIndex = NWIndex(theProject) theMD = ToMarkdown(theProject) - theMD.isNovel = True - theMD.isNote = False + theMD._isNovel = True + theMD._isNote = False # Special Titles # ============== # Title - theMD.theTokens = [ + theMD._theTokens = [ (theMD.T_TITLE, 1, "A Title", None, theMD.A_PBB | theMD.A_CENTRE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE), ] @@ -179,7 +179,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI): assert theMD.theResult == "# A Title\n\n" # Unnumbered - theMD.theTokens = [ + theMD._theTokens = [ (theMD.T_UNNUM, 1, "Prologue", None, theMD.A_PBB), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE), ] @@ -190,7 +190,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI): # ========== # Separator - theMD.theTokens = [ + theMD._theTokens = [ (theMD.T_SEP, 1, "* * *", None, theMD.A_CENTRE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE), ] @@ -198,7 +198,7 @@ def testCoreToMarkdown_ConvertDirect(mockGUI): assert theMD.theResult == "* * *\n\n" # Skip - theMD.theTokens = [ + theMD._theTokens = [ (theMD.T_SKIP, 1, "", None, theMD.A_NONE), (theMD.T_EMPTY, 1, "", None, theMD.A_NONE), ] @@ -214,7 +214,7 @@ def testCoreToMarkdown_Complex(mockGUI, fncDir): """ theProject = NWProject(mockGUI) theMD = ToMarkdown(theProject) - theMD.isNovel = True + theMD._isNovel = True # Build Project # ============= @@ -239,7 +239,7 @@ def testCoreToMarkdown_Complex(mockGUI, fncDir): ] for i in range(len(docText)): - theMD.theText = docText[i] + theMD._theText = docText[i] theMD.doPreProcessing() theMD.tokenizeText() theMD.doConvert() diff --git a/tests/test_core/test_core_toodt.py b/tests/test_core/test_core_toodt.py index ee6f65af..2b564350 100644 --- a/tests/test_core/test_core_toodt.py +++ b/tests/test_core/test_core_toodt.py @@ -236,7 +236,7 @@ def testCoreToOdt_Convert(mockGUI): mockGUI.theIndex = NWIndex(theProject) theDoc = ToOdt(theProject, isFlat=True) - theDoc.isNovel = True + theDoc._isNovel = True def getStyle(styleName): for aSet in theDoc._autoPara.values(): @@ -248,7 +248,7 @@ def testCoreToOdt_Convert(mockGUI): # ======= # Header 1 - theDoc.theText = "# Title\n" + theDoc._theText = "# Title\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -261,7 +261,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Header 2 - theDoc.theText = "## Chapter\n" + theDoc._theText = "## Chapter\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -274,7 +274,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Header 3 - theDoc.theText = "### Scene\n" + theDoc._theText = "### Scene\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -287,7 +287,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Header 4 - theDoc.theText = "#### Section\n" + theDoc._theText = "#### Section\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -300,7 +300,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Title - theDoc.theText = "#! Title\n" + theDoc._theText = "#! Title\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -313,7 +313,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Unnumbered chapter - theDoc.theText = "##! Prologue\n" + theDoc._theText = "##! Prologue\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -329,7 +329,7 @@ def testCoreToOdt_Convert(mockGUI): # ========== # Nested Text - theDoc.theText = "Some ~~nested **bold** and _italics_ text~~ text." + theDoc._theText = "Some ~~nested **bold** and _italics_ text~~ text." theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -347,7 +347,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Hard Break - theDoc.theText = "Some text.\nNext line\n" + theDoc._theText = "Some text.\nNext line\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -360,7 +360,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Tab - theDoc.theText = "\tItem 1\tItem 2\n" + theDoc._theText = "\tItem 1\tItem 2\n" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -373,7 +373,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Tab in Format - theDoc.theText = "Some **bold\ttext**" + theDoc._theText = "Some **bold\ttext**" theDoc.tokenizeText() theDoc.initDocument() theDoc.doConvert() @@ -387,7 +387,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Multiple Spaces - theDoc.theText = ( + theDoc._theText = ( "### Scene\n\n" "Hello World\n\n" "Hello World\n\n" @@ -408,7 +408,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Synopsis, Comment, Keywords - theDoc.theText = ( + theDoc._theText = ( "### Scene\n\n" "@pov: Jane\n\n" "% synopsis: So it begins\n\n" @@ -435,7 +435,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Scene Separator - theDoc.theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" + theDoc._theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" theDoc.setSceneFormat("* * *", False) theDoc.tokenizeText() theDoc.doHeaders() @@ -453,7 +453,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Scene Break - theDoc.theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" + theDoc._theText = "### Scene One\n\nText\n\n### Scene Two\n\nText" theDoc.setSceneFormat("", False) theDoc.tokenizeText() theDoc.doHeaders() @@ -471,7 +471,7 @@ def testCoreToOdt_Convert(mockGUI): ) # Paragraph Styles - theDoc.theText = ( + theDoc._theText = ( "### Scene\n\n" "@pov: Jane\n" "@char: John\n" @@ -513,7 +513,7 @@ def testCoreToOdt_Convert(mockGUI): assert getStyle("P8")._pAttr["margin-right"] == ["fo", "1.693cm"] # Justified - theDoc.theText = ( + theDoc._theText = ( "### Scene\n\n" "Regular paragraph\n\n" "with\nbreak\n\n" @@ -536,7 +536,7 @@ def testCoreToOdt_Convert(mockGUI): assert getStyle("P9")._pAttr["text-align"] == ["fo", "left"] # Page Breaks - theDoc.theText = ( + theDoc._theText = ( "## Chapter One\n\n" "Text\n\n" "## Chapter Two\n\n" @@ -568,11 +568,11 @@ def testCoreToOdt_ConvertDirect(mockGUI): mockGUI.theIndex = NWIndex(theProject) theDoc = ToOdt(theProject, isFlat=True) - theDoc.isNovel = True + theDoc._isNovel = True # Justified theDoc = ToOdt(theProject, isFlat=True) - theDoc.theTokens = [ + theDoc._theTokens = [ (theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_JUSTIFY), (theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE), ] @@ -593,7 +593,7 @@ def testCoreToOdt_ConvertDirect(mockGUI): # Page Break After theDoc = ToOdt(theProject, isFlat=True) - theDoc.theTokens = [ + theDoc._theTokens = [ (theDoc.T_TEXT, 1, "This is a paragraph", [], theDoc.A_PBA), (theDoc.T_EMPTY, 1, "", None, theDoc.A_NONE), ] @@ -623,12 +623,12 @@ def testCoreToOdt_SaveFlat(mockGUI, fncDir, outDir, refDir): mockGUI.theIndex = NWIndex(theProject) theDoc = ToOdt(theProject, isFlat=True) - theDoc.isNovel = True + theDoc._isNovel = True assert theDoc.setLanguage(None) is False assert theDoc.setLanguage("nb_NO") is True theDoc.setColourHeaders(True) - theDoc.theText = ( + theDoc._theText = ( "## Chapter One\n\n" "Text\n\n" "## Chapter Two\n\n" @@ -660,9 +660,9 @@ def testCoreToOdt_SaveFull(mockGUI, fncDir, outDir, refDir): mockGUI.theIndex = NWIndex(theProject) theDoc = ToOdt(theProject, isFlat=False) - theDoc.isNovel = True + theDoc._isNovel = True - theDoc.theText = ( + theDoc._theText = ( "## Chapter One\n\n" "Text\n\n" "## Chapter Two\n\n"