Merge pull request #447 from vkbo/code_improvements

Code Improvements
This commit is contained in:
Veronica K. Berglyd Olsen
2020-09-18 18:55:55 +02:00
committed by GitHub
17 changed files with 339 additions and 171 deletions
+12
View File
@@ -82,6 +82,18 @@ def checkBool(checkValue, defaultValue, allowNone=False):
return defaultValue
return defaultValue
def checkHandle(checkValue, defaultValue, allowNone=False):
"""Check if a value is a handle.
"""
if allowNone:
if checkValue is None:
return None
if checkValue == "None":
return None
if isHandle(checkValue):
return str(checkValue)
return defaultValue
def isHandle(theString):
"""Check if a string is a valid novelWriter handle.
Note: This is case sensitive. Must be lower case!
-2
View File
@@ -4,7 +4,6 @@ from nw.core.document import NWDoc
from nw.core.index import NWIndex
from nw.core.project import NWProject
from nw.core.spellcheck import NWSpellCheck, NWSpellEnchant, NWSpellSimple
from nw.core.tokenizer import Tokenizer
from nw.core.tohtml import ToHtml
from nw.core.tools import countWords, numberToRoman, numberToWord
@@ -15,7 +14,6 @@ __all__ = [
"NWSpellCheck",
"NWSpellEnchant",
"NWSpellSimple",
"Tokenizer",
"ToHtml",
"countWords",
"numberToRoman",
+41 -33
View File
@@ -26,11 +26,9 @@
"""
import logging
import nw
from os import path, rename, unlink
from nw.core.item import NWItem
from nw.constants import nwAlert
from nw.common import isHandle
from nw.constants import nwItemLayout, nwItemClass, nwConst
@@ -41,14 +39,14 @@ class NWDoc():
def __init__(self, theProject, theParent):
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.theItem = None
self.docHandle = None
self.fileLoc = None
self.docMeta = ""
# Internal Variables
self._theItem = None # The currently open item
self._docHandle = None # The handle of the currently open item
self._fileLoc = None # The file location of the currently open item
self._docMeta = "" # The meta string of the currently open item
# Internal Mapping
self.makeAlert = self.theParent.makeAlert
@@ -62,10 +60,10 @@ class NWDoc():
def clearDocument(self):
"""Clear the document contents.
"""
self.theItem = None
self.docHandle = None
self.fileLoc = None
self.docMeta = ""
self._theItem = None
self._docHandle = None
self._fileLoc = None
self._docMeta = ""
return
def openDocument(self, tHandle, showStatus=True, isOrphan=False):
@@ -78,31 +76,31 @@ class NWDoc():
# Always clear first, since the object will often be reused.
self.clearDocument()
self.docHandle = tHandle
self._docHandle = tHandle
if not isOrphan:
self.theItem = self.theProject.projTree[tHandle]
self._theItem = self.theProject.projTree[tHandle]
else:
self.theItem = None
self._theItem = None
if self.theItem is None and not isOrphan:
if self._theItem is None and not isOrphan:
self.clearDocument()
return None
docFile = self.docHandle+".nwd"
docFile = self._docHandle+".nwd"
logger.debug("Opening document %s" % docFile)
docPath = path.join(self.theProject.projContent, docFile)
self.fileLoc = docPath
self._fileLoc = docPath
theText = ""
self.docMeta = ""
self._docMeta = ""
if path.isfile(docPath):
try:
with open(docPath, mode="r", encoding="utf8") as inFile:
fstLine = inFile.readline()
if fstLine.startswith("%%~ "):
# This is the meta line
self.docMeta = fstLine[4:].strip()
self._docMeta = fstLine[4:].strip()
else:
theText = fstLine
theText += inFile.read()
@@ -120,10 +118,10 @@ class NWDoc():
logger.debug("The requested document does not exist.")
return ""
logger.verbose("DocMeta: '%s'" % self.docMeta)
logger.verbose("DocMeta: '%s'" % self._docMeta)
if showStatus and not isOrphan:
self.theParent.statusBar.setStatus("Opened Document: %s" % self.theItem.itemName)
self.theParent.statusBar.setStatus("Opened Document: %s" % self._theItem.itemName)
return theText
@@ -131,29 +129,29 @@ class NWDoc():
"""Save the document via temp file in case of save failure, and
in any case keep a backup of the file.
"""
if self.docHandle is None:
if self._docHandle is None:
return False
self.theProject.ensureFolderStructure()
docFile = self.docHandle+".nwd"
docFile = self._docHandle+".nwd"
logger.debug("Saving document %s" % docFile)
docPath = path.join(self.theProject.projContent, docFile)
docTemp = path.join(self.theProject.projContent, docFile+"~")
if isinstance(self.theItem, NWItem):
itemPath = self.theProject.projTree.getItemPath(self.docHandle)
if self._theItem is None:
docMeta = ""
else:
itemPath = self.theProject.projTree.getItemPath(self._docHandle)
docMeta = (
"%%~ {handlepath:s}:{itemclass:s}:{itemlayout:s}:{itemname:s}\n"
).format(
handlepath = ":".join(itemPath),
itemclass = self.theItem.itemClass.name,
itemlayout = self.theItem.itemLayout.name,
itemname = self.theItem.itemName,
itemclass = self._theItem.itemClass.name,
itemlayout = self._theItem.itemLayout.name,
itemname = self._theItem.itemName,
)
else:
docMeta = ""
try:
with open(docTemp, mode="w", encoding="utf8") as outFile:
@@ -169,7 +167,7 @@ class NWDoc():
unlink(docPath)
rename(docTemp, docPath)
self.theParent.statusBar.setStatus("Saved Document: %s" % self.theItem.itemName)
self.theParent.statusBar.setStatus("Saved Document: %s" % self._theItem.itemName)
return True
@@ -201,15 +199,25 @@ class NWDoc():
# Getters
##
def getFileLocation(self):
"""Return the file location of the current file.
"""
return self._fileLoc
def getCurrentItem(self):
"""Return a pointer to the currently open item.
"""
return self._theItem
def getMeta(self):
"""Parses the document meta tag and returns the path and name as
a list and a string.
"""
if len(self.docMeta) < 14:
if len(self._docMeta) < 14:
# Not enough information
return "", [], None, None
theMeta = self.docMeta
theMeta = self._docMeta
# Scan for handles
thePath = []
+2 -1
View File
@@ -27,6 +27,7 @@
import logging
import json
import nw
from os import path
from time import time
@@ -66,9 +67,9 @@ class NWIndex():
def __init__(self, theProject, theParent):
# Internal
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.mainConf = self.theParent.mainConf
self.indexBroken = False
# Indices
-3
View File
@@ -28,7 +28,6 @@
import logging
import json
import nw
from os import path
@@ -40,9 +39,7 @@ class OptionState():
def __init__(self, theProject):
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theState = {}
self.validMap = {
"GuiWritingStats": {
+27 -25
View File
@@ -481,7 +481,7 @@ class NWProject():
# Changes:
# 1.0 : Original file format.
# 1.1 : Changes the way documents are structure in the project
# 1.1 : Changes the way documents are structured in the project
# folder from data_X, where X is the first hex value of
# the handle, to a single content folder.
# 1.2 : Changes the way autoReplace entries are stored. The 1.1
@@ -518,15 +518,16 @@ class NWProject():
msgRes = msgBox.question(self.theParent, "Version Conflict", (
"This project was saved by a newer version of novelWriter, version %s. "
"This is version %s. If you continue to open the project, some attributes "
"and settings may not be preserved. Continue opening the project?"
"and settings may not be preserved, but the overall project should be fine. "
"Continue opening the project?"
) % (
appVersion, nw.__version__
))
if msgRes != QMessageBox.Yes:
return False
# Start Parsing XML
# =================
# Start Parsing the XML
# =====================
for xChild in xRoot:
if xChild.tag == "project":
@@ -578,9 +579,9 @@ class NWProject():
elif xItem.tag == "notesWordCount":
self.notesWCount = checkInt(xItem.text, 0, False)
elif xItem.tag == "status":
self.statusItems.unpackEntries(xItem)
self.statusItems.unpackXML(xItem)
elif xItem.tag == "importance":
self.importItems.unpackEntries(xItem)
self.importItems.unpackXML(xItem)
elif xItem.tag == "autoReplace":
for xEntry in xItem:
if xEntry.tag == "entry":
@@ -684,9 +685,9 @@ class NWProject():
self._packProjectValue(xTitleFmt, aKey, aValue)
xStatus = etree.SubElement(xSettings, "status")
self.statusItems.packEntries(xStatus)
self.statusItems.packXML(xStatus)
xStatus = etree.SubElement(xSettings, "importance")
self.importItems.packEntries(xStatus)
self.importItems.packXML(xStatus)
# Save Tree Content
logger.debug("Writing project content")
@@ -1440,23 +1441,24 @@ class NWProject():
def _deprecatedFiles(self):
"""Delete files that are no longer used by novelWriter.
"""
rmList = []
rmList.append(path.join(self.projCache, "nwProject.nwx.0"))
rmList.append(path.join(self.projCache, "nwProject.nwx.1"))
rmList.append(path.join(self.projCache, "nwProject.nwx.2"))
rmList.append(path.join(self.projCache, "nwProject.nwx.3"))
rmList.append(path.join(self.projCache, "nwProject.nwx.4"))
rmList.append(path.join(self.projCache, "nwProject.nwx.5"))
rmList.append(path.join(self.projCache, "nwProject.nwx.6"))
rmList.append(path.join(self.projCache, "nwProject.nwx.7"))
rmList.append(path.join(self.projCache, "nwProject.nwx.8"))
rmList.append(path.join(self.projCache, "nwProject.nwx.9"))
rmList.append(path.join(self.projMeta, "mainOptions.json"))
rmList.append(path.join(self.projMeta, "exportOptions.json"))
rmList.append(path.join(self.projMeta, "outlineOptions.json"))
rmList.append(path.join(self.projMeta, "timelineOptions.json"))
rmList.append(path.join(self.projMeta, "docMergeOptions.json"))
rmList.append(path.join(self.projMeta, "sessionLogOptions.json"))
rmList = [
path.join(self.projCache, "nwProject.nwx.0"),
path.join(self.projCache, "nwProject.nwx.1"),
path.join(self.projCache, "nwProject.nwx.2"),
path.join(self.projCache, "nwProject.nwx.3"),
path.join(self.projCache, "nwProject.nwx.4"),
path.join(self.projCache, "nwProject.nwx.5"),
path.join(self.projCache, "nwProject.nwx.6"),
path.join(self.projCache, "nwProject.nwx.7"),
path.join(self.projCache, "nwProject.nwx.8"),
path.join(self.projCache, "nwProject.nwx.9"),
path.join(self.projMeta, "mainOptions.json"),
path.join(self.projMeta, "exportOptions.json"),
path.join(self.projMeta, "outlineOptions.json"),
path.join(self.projMeta, "timelineOptions.json"),
path.join(self.projMeta, "docMergeOptions.json"),
path.join(self.projMeta, "sessionLogOptions.json"),
]
for rmFile in rmList:
if path.isfile(rmFile):
+2 -5
View File
@@ -89,13 +89,10 @@ class NWSpellCheck():
@staticmethod
def expandLanguage(spTag):
"""Translate a language tag to something more suer friendly.
"""Translate a language tag to something more user friendly.
"""
spBits = spTag.split("_")
if spBits[0] in isoLanguage.ISO_639_1:
spLang = isoLanguage.ISO_639_1[spBits[0]]
else:
spLang = spBits[0]
spLang = isoLanguage.ISO_639_1.get(spBits[0], spBits[0])
if len(spBits) > 1:
spLang += " (%s)" % spBits[1]
return spLang
+42 -42
View File
@@ -36,12 +36,12 @@ logger = logging.getLogger(__name__)
class NWStatus():
def __init__(self):
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
return
def addEntry(self, theLabel, theColours):
@@ -50,11 +50,11 @@ class NWStatus():
"""
theLabel = theLabel.strip()
if self.lookupEntry(theLabel) is None:
self.theLabels.append(theLabel)
self.theColours.append(theColours)
self.theCounts.append(0)
self.theMap[theLabel] = self.theLength
self.theLength += 1
self._theLabels.append(theLabel)
self._theColours.append(theColours)
self._theCounts.append(0)
self._theMap[theLabel] = self._theLength
self._theLength += 1
return True
def lookupEntry(self, theLabel):
@@ -64,8 +64,8 @@ class NWStatus():
if theLabel is None:
return None
theLabel = theLabel.strip()
if theLabel in self.theMap.keys():
return self.theMap[theLabel]
if theLabel in self._theMap.keys():
return self._theMap[theLabel]
return None
def checkEntry(self, theStatus):
@@ -77,8 +77,8 @@ class NWStatus():
if self.lookupEntry(theStatus) is not None:
return theStatus
theStatus = checkInt(theStatus, 0, False)
if theStatus >= 0 and theStatus < self.theLength:
return self.theLabels[theStatus]
if theStatus >= 0 and theStatus < self._theLength:
return self._theLabels[theStatus]
def setNewEntries(self, newList):
"""Update the list of entries after they have been modified by
@@ -87,12 +87,12 @@ class NWStatus():
replaceMap = {}
if newList is not None:
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
for nName, nR, nG, nB, oName in newList:
self.addEntry(nName, (nR, nG, nB))
@@ -104,7 +104,7 @@ class NWStatus():
def resetCounts(self):
"""Clear the counts of references to the status entries.
"""
self.theCounts = [0]*self.theLength
self._theCounts = [0]*self._theLength
return
def countEntry(self, theLabel):
@@ -112,23 +112,23 @@ class NWStatus():
"""
theIndex = self.lookupEntry(theLabel)
if theIndex is not None:
self.theCounts[theIndex] += 1
self._theCounts[theIndex] += 1
return
def packEntries(self, xParent):
def packXML(self, xParent):
"""Pack the status entries into an XML object for saving to the
main project file.
"""
for n in range(self.theLength):
for n in range(self._theLength):
xSub = etree.SubElement(xParent, "entry", attrib={
"blue" : str(self.theColours[n][2]),
"green" : str(self.theColours[n][1]),
"red" : str(self.theColours[n][0]),
"blue" : str(self._theColours[n][2]),
"green" : str(self._theColours[n][1]),
"red" : str(self._theColours[n][0]),
})
xSub.text = self.theLabels[n]
xSub.text = self._theLabels[n]
return True
def unpackEntries(self, xParent):
def unpackXML(self, xParent):
"""Unpack an XML tree and set the class values.
"""
theLabels = []
@@ -151,12 +151,12 @@ class NWStatus():
theColours.append((cR, cG, cB))
if len(theLabels) > 0:
self.theLabels = []
self.theColours = []
self.theCounts = []
self.theMap = {}
self.theLength = 0
self.theIndex = 0
self._theLabels = []
self._theColours = []
self._theCounts = []
self._theMap = {}
self._theLength = 0
self._theIndex = 0
for n in range(len(theLabels)):
self.addEntry(theLabels[n], theColours[n])
@@ -170,22 +170,22 @@ class NWStatus():
def __getitem__(self, n):
"""Return an entry by its index.
"""
if n >= 0 and n < self.theLength:
return self.theLabels[n], self.theColours[n], self.theCounts[n]
if n >= 0 and n < self._theLength:
return self._theLabels[n], self._theColours[n], self._theCounts[n]
return None, None, None
def __iter__(self):
"""Initialise the iterator.
"""
self.theIndex = 0
self._theIndex = 0
return self
def __next__(self):
"""Return the next entry for the iterator.
"""
if self.theIndex < self.theLength:
theLabel, theColour, theCount = self.__getitem__(self.theIndex)
self.theIndex += 1
if self._theIndex < self._theLength:
theLabel, theColour, theCount = self.__getitem__(self._theIndex)
self._theIndex += 1
return theLabel, theColour, theCount
else:
raise StopIteration
+1 -2
View File
@@ -309,8 +309,7 @@ class ToHtml(Tokenizer):
def _formatKeywords(self, tText):
"""Apply HTML formatting to keywords.
"""
tText = "@"+tText
isValid, theBits, thePos = self.theParent.theIndex.scanThis(tText)
isValid, theBits, thePos = self.theParent.theIndex.scanThis("@"+tText)
if not isValid or not theBits:
return ""
+2 -4
View File
@@ -27,7 +27,6 @@
import logging
import re
import nw
from operator import itemgetter
from PyQt5.QtCore import QRegularExpression
@@ -74,9 +73,8 @@ class Tokenizer():
def __init__(self, theProject, theParent):
self.mainConf = nw.CONFIG
self.theProject = theProject
self.theParent = theParent
self.theProject = theProject
self.theParent = theParent
# Data Variables
self.theText = None # The raw text to be tokenized
+24 -25
View File
@@ -34,7 +34,7 @@ from hashlib import sha256
from time import time
from nw.core.item import NWItem
from nw.common import checkString
from nw.common import checkHandle
from nw.constants import nwFiles, nwItemType, nwItemClass, nwItemLayout, nwConst
logger = logging.getLogger(__name__)
@@ -49,7 +49,6 @@ class NWTree():
self._treeOrder = [] # The order of the tree items on the tree view
self._treeRoots = [] # The root items of the tree
self._trashRoot = None # The handle of the trash root folder
self._theLength = 0 # Always the length of _treeOrder
self._theIndex = 0 # The current iterator index
self._treeChanged = False # True if tree structure has changed
self._handleSeed = None # Used for generating handles for testing
@@ -68,7 +67,6 @@ class NWTree():
self._treeRoots = []
self._trashRoot = None
self._archRoot = None
self._theLength = 0
self._theIndex = 0
self._treeChanged = False
return
@@ -81,12 +79,12 @@ class NWTree():
def append(self, tHandle, pHandle, nwItem):
"""Add a new item to the end of the tree.
"""
tHandle = checkString(tHandle, None, True)
pHandle = checkString(pHandle, None, True)
tHandle = checkHandle(tHandle, None, True)
pHandle = checkHandle(pHandle, None, True)
if tHandle is None:
tHandle = self._makeHandle()
logger.verbose("Adding entry %s with parent %s" % (str(tHandle), str(pHandle)))
logger.verbose("Adding item %s with parent %s" % (str(tHandle), str(pHandle)))
nwItem.setHandle(tHandle)
nwItem.setParent(pHandle)
@@ -95,29 +93,29 @@ class NWTree():
self._treeOrder.append(tHandle)
if nwItem.itemType == nwItemType.ROOT:
logger.verbose("Entry %s is a root item" % str(tHandle))
logger.verbose("Item %s is a root item" % str(tHandle))
self._treeRoots.append(tHandle)
if nwItem.itemClass == nwItemClass.ARCHIVE:
logger.verbose("Entry %s is the archive folder" % str(tHandle))
logger.verbose("Item %s is the archive folder" % str(tHandle))
self._archRoot = tHandle
if nwItem.itemType == nwItemType.TRASH:
if self._trashRoot is None:
logger.verbose("Entry %s is the trash folder" % str(tHandle))
logger.verbose("Item %s is the trash folder" % str(tHandle))
self._trashRoot = tHandle
else:
logger.error("Only one trash folder allowed")
self._theLength = len(self._treeOrder)
self._setTreeChanged(True)
return
def packXML(self, xParent):
"""Pack the content of the tree into an XML object.
"""Pack the content of the tree into the provided XML object. In
the order defined by the _treeOrder list.
"""
xContent = etree.SubElement(xParent, "content", attrib={
"count": str(self._theLength)}
"count": str(len(self._treeOrder))}
)
for tHandle in self._treeOrder:
tItem = self.__getitem__(tHandle)
@@ -309,7 +307,6 @@ class NWTree():
# Save the temp list
self._treeOrder = tmpOrder
self._theLength = len(self._treeOrder)
self._setTreeChanged(True)
logger.verbose("Project tree order updated")
@@ -330,7 +327,7 @@ class NWTree():
if tItem is None:
return False
if tItem.itemType != nwItemType.FILE:
logger.error("Item '%s' is not a file" % tHandle)
logger.error("Item %s is not a file" % tHandle)
return False
if not isinstance(itemLayout, nwItemLayout):
return False
@@ -370,12 +367,12 @@ class NWTree():
def __len__(self):
"""Return the length counter. Does not check that it is correct!
"""
return self._theLength
return len(self._treeOrder)
def __bool__(self):
"""Returns True if the tree has any entries.
"""
return self._theLength > 0
return len(self._treeOrder) > 0
##
# Item Access Methods
@@ -391,19 +388,21 @@ class NWTree():
return None
def __delitem__(self, tHandle):
"""This only removes the item from the order list, but not from
the project tree.
"""Remove an item from the internal lists and dictionaries.
"""
if tHandle not in self._treeOrder:
logger.warning(
"Could not remove item %s from project tree as it does not exist" % tHandle
)
if tHandle in self._treeOrder and tHandle in self._projTree:
self._treeOrder.remove(tHandle)
del self._projTree[tHandle]
else:
logger.warning("Failed to delete item %s: item not found" % tHandle)
return False
self._treeOrder.remove(tHandle)
self._theLength = len(self._treeOrder)
if tHandle in self._treeRoots:
self._treeRoots.remove(tHandle)
if tHandle == self._trashRoot:
self._trashRoot = None
if tHandle == self._archRoot:
self._archRoot = None
self._setTreeChanged(True)
@@ -427,7 +426,7 @@ class NWTree():
def __next__(self):
"""Returns the item from the next entry in the _treeOrder list.
"""
if self._theIndex < self._theLength:
if self._theIndex < len(self._treeOrder):
theItem = self.__getitem__(self._treeOrder[self._theIndex])
self._theIndex += 1
return theItem
+17 -15
View File
@@ -270,10 +270,12 @@ class GuiDocEditor(QTextEdit):
afTime = time()
logger.debug("Document highlighted in %.3f milliseconds" % (1000*(afTime-bfTime)))
if tLine is None:
self.setCursorPosition(self.nwDocument.theItem.cursorPos)
theItem = self.nwDocument.getCurrentItem()
if tLine is None and theItem is not None:
self.setCursorPosition(theItem.cursorPos)
else:
self.setCursorLine(tLine)
self.lastEdit = time()
self._runCounter()
self.wcTimer.start()
@@ -308,21 +310,20 @@ class GuiDocEditor(QTextEdit):
"""Save the text currently in the editor to the NWDoc object,
and update the NWItem meta data.
"""
if self.nwDocument.theItem is None:
theItem = self.nwDocument.getCurrentItem()
if theItem is None:
return False
docText = self.getText()
cursPos = self.getCursorPosition()
self.nwDocument.theItem.setCharCount(self.charCount)
self.nwDocument.theItem.setWordCount(self.wordCount)
self.nwDocument.theItem.setParaCount(self.paraCount)
self.nwDocument.theItem.setCursorPos(cursPos)
theItem.setCharCount(self.charCount)
theItem.setWordCount(self.wordCount)
theItem.setParaCount(self.paraCount)
theItem.setCursorPos(cursPos)
self.nwDocument.saveDocument(docText)
self.setDocumentChanged(False)
self.theParent.theIndex.scanText(
self.nwDocument.theItem.itemHandle, docText
)
self.theParent.theIndex.scanText(theItem.itemHandle, docText)
return True
@@ -598,7 +599,7 @@ class GuiDocEditor(QTextEdit):
"Location: {fileLoc:s}"
).format(
handle = self.theHandle,
fileLoc = str(self.nwDocument.fileLoc)
fileLoc = str(self.nwDocument.getFileLocation())
))
return
@@ -873,7 +874,8 @@ class GuiDocEditor(QTextEdit):
def _updateCounts(self):
"""Slot for the word counter's finished signal
"""
if self.theHandle is None or self.nwDocument.theItem is None:
theItem = self.nwDocument.getCurrentItem()
if self.theHandle is None or theItem is None:
return
logger.verbose("Updating word count")
@@ -881,9 +883,9 @@ class GuiDocEditor(QTextEdit):
self.charCount = self.wCounter.charCount
self.wordCount = self.wCounter.wordCount
self.paraCount = self.wCounter.paraCount
self.nwDocument.theItem.setCharCount(self.charCount)
self.nwDocument.theItem.setWordCount(self.wordCount)
self.nwDocument.theItem.setParaCount(self.paraCount)
theItem.setCharCount(self.charCount)
theItem.setWordCount(self.wordCount)
theItem.setParaCount(self.paraCount)
self.theParent.treeView.propagateCount(self.theHandle, self.wordCount)
self.theParent.treeView.projectWordCount()
+7 -4
View File
@@ -639,7 +639,8 @@ class GuiDocViewHistory():
return
def _truncateHistory(self, atPos):
"""Truncate the navigation history to the given position.
"""Truncate the navigation history to the given position. Also
enforces a maximum length of the navigation history to 20.
"""
nSkip = 1 if atPos > 19 else 0
@@ -652,13 +653,15 @@ class GuiDocViewHistory():
return
def _dumpHistory(self):
"""Debug function to dump history. Since it is a for loop, it is
skipped entirely if log level isn't VERBOSE.
"""Debug function to dump history to the logger. Since it is a
for loop, it is skipped entirely if log level isn't VERBOSE.
"""
if logger.getEffectiveLevel() < logging.DEBUG:
for i, (h, p) in enumerate(zip(self._navHistory, self._posHistory)):
logger.verbose(
"History: %s %2d %13s %5d" % (">" if i == self._currPos else " ", i, h, p)
"History %02d: %s %13s [x:%d]" % (
i + 1, ">" if i == self._currPos else " ", h, p
)
)
return
+7 -7
View File
@@ -378,11 +378,11 @@ class GuiProjectTree(QTreeWidget):
return True
def deleteItem(self, tHandle=None, alreadyAsked=False, askForTrash=False):
"""Delete items from the tree. Note that this does not delete
the item from the item tree in the project object. However,
since this is only meta data, there isn't really a need to do
that to save memory. Items not in the tree are not saved to the
project file, so a loaded project will be clean anyway.
"""Delete an item from the project tree. As a first step, files are
moved to the Trash folder. Permanent deletion is a second step. This
second step also deletes the item from the project object as well as
delete the files on disk. Folders are deleted if they're empty only,
and the deletion is always permanent.
"""
if tHandle is None:
tHandle = self.getSelectedHandle()
@@ -584,7 +584,7 @@ class GuiProjectTree(QTreeWidget):
always make sure items with a parent have had their parent item
sent first.
"""
logger.debug("Building project tree ...")
logger.debug("Building the project tree ...")
self.clear()
iCount = 0
@@ -592,7 +592,7 @@ class GuiProjectTree(QTreeWidget):
iCount += 1
self._addTreeItem(nwItem)
logger.debug("%d items added to project tree" % iCount)
logger.debug("%d items added to the project tree" % iCount)
return True
def getSelectedHandle(self):
+10 -1
View File
@@ -5,7 +5,7 @@
import pytest
from nw.common import (
checkString, checkBool, checkInt, colRange, formatInt, transferCase,
fuzzyTime
fuzzyTime, checkHandle
)
from nwtools import cmpList
@@ -42,6 +42,15 @@ def testCheckBool():
assert checkBool(1.0, None, False) is None
assert checkBool(2.0, None, False) is None
@pytest.mark.core
def testCheckHandle():
assert checkHandle("None", 1, True) is None
assert checkHandle("None", 1, False) == 1
assert checkHandle(None, 1, True) is None
assert checkHandle(None, 1, False) == 1
assert checkHandle("47666c91c7ccf", None, False) == "47666c91c7ccf"
assert checkHandle("h7666c91c7ccf", None, False) is None
@pytest.mark.core
def testColRange():
assert colRange([0, 0], [0, 0], 0) is None
+61 -2
View File
@@ -12,7 +12,7 @@ from nw.core.project import NWProject
from nw.core.document import NWDoc
from nw.core.index import NWIndex
from nw.core.spellcheck import NWSpellEnchant, NWSpellSimple
from nw.constants import nwItemClass, nwItemLayout
from nw.constants import nwItemClass, nwItemLayout, nwFiles
@pytest.mark.project
def testProjectNewOpenSave(nwFuncTemp, nwTempProj, nwRef, nwTemp, nwDummy):
@@ -316,7 +316,7 @@ def testDocMeta(nwDummy, nwLipsum):
assert theClass == nwItemClass.NOVEL
assert theLayout == nwItemLayout.SCENE
aDoc.docMeta = "too_short"
aDoc._docMeta = "too_short"
theMeta, thePath, theClass, theLayout = aDoc.getMeta()
assert theMeta == ""
assert thePath == []
@@ -372,3 +372,62 @@ def testSpellSimple(nwTemp, nwConf):
dList = spChk.listDictionaries()
assert len(dList) > 0
@pytest.mark.project
def testProjectOptions(nwDummy, nwLipsum):
theProject = NWProject(nwDummy)
assert theProject.projMeta is None
theOpts = theProject.optState
assert not theOpts.loadSettings()
assert not theOpts.saveSettings()
# No Settings
assert theProject.openProject(nwLipsum)
assert theOpts.loadSettings()
assert theOpts.saveSettings()
assert str(theOpts.theState) == r"{}"
# Read Invalid Settings and Filter
stateFile = path.join(theProject.projMeta, nwFiles.OPTS_FILE)
with open(stateFile, mode="w", encoding="utf8") as outFile:
outFile.write(
r'{"GuiProjectSettings": {"winWidth": 100, "winHeight": 50}, "NoGroup": {"NoName": 0}}'
)
assert theOpts.loadSettings()
assert str(theOpts.theState) == r"{'GuiProjectSettings': {'winWidth': 100, 'winHeight': 50}}"
# Set New Settings
assert not theOpts.setValue("NoGroup", "NoName", None)
assert not theOpts.setValue("GuiProjectSettings", "NoName", None)
assert theOpts.setValue("GuiProjectSettings", "winWidth", 200)
assert theOpts.setValue("GuiProjectSettings", "winHeight", 80)
assert str(theOpts.theState) == r"{'GuiProjectSettings': {'winWidth': 200, 'winHeight': 80}}"
# Check Read/Write Types
## String
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
assert isinstance(theOpts.getString("GuiWritingStats", "winWidth", "456"), str)
assert theOpts.getString("GuiWritingStats", "NoName", "456") == "456"
## Int
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
assert isinstance(theOpts.getInt("GuiWritingStats", "winWidth", 456), int)
assert theOpts.getInt("GuiWritingStats", "NoName", 456) == 456
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
assert theOpts.getInt("GuiWritingStats", "NoName", 456) == 456
## Float
assert theOpts.setValue("GuiWritingStats", "winWidth", "123")
assert isinstance(theOpts.getFloat("GuiWritingStats", "winWidth", 456.0), float)
assert theOpts.getFloat("GuiWritingStats", "NoName", 456.0) == 456.0
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
assert theOpts.getFloat("GuiWritingStats", "winWidth", 456.0) == 456.0
## Bool
assert theOpts.setValue("GuiWritingStats", "winWidth", True)
assert isinstance(theOpts.getBool("GuiWritingStats", "winWidth", False), bool)
assert theOpts.getFloat("GuiWritingStats", "NoName", False) is False
assert theOpts.setValue("GuiWritingStats", "winWidth", "True")
assert theOpts.getFloat("GuiWritingStats", "winWidth", False) is False
+84
View File
@@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
"""novelWriter Tools Tester
"""
import pytest
from nw.core.tools import countWords, numberToRoman, numberToWord
@pytest.mark.core
def testCountWords():
testText = (
"# Heading One\n"
"## Heading Two\n"
"### Heading Three\n"
"#### Heading Four\n"
"\n"
"@tag: value\n"
"\n"
"% A comment that should n ot be counted.\n"
"\n"
"The first paragraph.\n"
"\n"
"The second paragraph.\n"
"\n"
"\n"
"The third paragraph.\n"
)
cC, wC, pC = countWords(testText)
assert cC == 108
assert wC == 17
assert pC == 3
@pytest.mark.core
def testNumberWords():
assert numberToWord(0, "en") == "Zero"
assert numberToWord(1, "en") == "One"
assert numberToWord(2, "en") == "Two"
assert numberToWord(3, "en") == "Three"
assert numberToWord(4, "en") == "Four"
assert numberToWord(5, "en") == "Five"
assert numberToWord(6, "en") == "Six"
assert numberToWord(7, "en") == "Seven"
assert numberToWord(8, "en") == "Eight"
assert numberToWord(9, "en") == "Nine"
assert numberToWord(10, "en") == "Ten"
assert numberToWord(11, "en") == "Eleven"
assert numberToWord(12, "en") == "Twelve"
assert numberToWord(13, "en") == "Thirteen"
assert numberToWord(14, "en") == "Fourteen"
assert numberToWord(15, "en") == "Fifteen"
assert numberToWord(16, "en") == "Sixteen"
assert numberToWord(17, "en") == "Seventeen"
assert numberToWord(18, "en") == "Eighteen"
assert numberToWord(19, "en") == "Nineteen"
assert numberToWord(20, "en") == "Twenty"
assert numberToWord(21, "en") == "Twenty-One"
assert numberToWord(29, "en") == "Twenty-Nine"
assert numberToWord(42, "en") == "Forty-Two"
assert numberToWord(142, "en") == "One Hundred Forty-Two"
assert numberToWord(999, "en") == "Nine Hundred Ninety-Nine"
@pytest.mark.core
def testRomanNumbers():
assert numberToRoman(None, False) == "NAN"
assert numberToRoman(0, False) == "OOR"
assert numberToRoman(1, False) == "I"
assert numberToRoman(2, False) == "II"
assert numberToRoman(3, False) == "III"
assert numberToRoman(4, False) == "IV"
assert numberToRoman(5, False) == "V"
assert numberToRoman(6, False) == "VI"
assert numberToRoman(7, False) == "VII"
assert numberToRoman(8, False) == "VIII"
assert numberToRoman(9, False) == "IX"
assert numberToRoman(10, False) == "X"
assert numberToRoman(14, False) == "XIV"
assert numberToRoman(42, False) == "XLII"
assert numberToRoman(99, False) == "XCIX"
assert numberToRoman(142, False) == "CXLII"
assert numberToRoman(542, False) == "DXLII"
assert numberToRoman(999, False) == "CMXCIX"
assert numberToRoman(2010, False) == "MMX"
assert numberToRoman(999, True) == "cmxcix"