Fix and optimise code (#904)
* Fix bug in early error reporting in main init * Update docstrings and optimise code in GuiMain * Update docstrings and optimise code in Config * Update docstrings and optimise code in common module * Update docstrings and optimise code in main project classes * Update the OptionState class * Update the spell checker class * Update the file converter classes and extend tests * Update the about, merge, split and item editor classes and extend tests * Update item editor test * Update about dialog tests * Some minor test cleanup * Fix typo and add clarification in contributing guide
This commit is contained in:
committed by
GitHub
parent
2a06db0a2b
commit
1c45331b0a
@@ -287,10 +287,8 @@ class NWIndex():
|
||||
nLine = 0
|
||||
nTitle = 0
|
||||
theLines = theText.splitlines()
|
||||
for aLine in theLines:
|
||||
nLine += 1
|
||||
nChar = len(aLine.strip())
|
||||
if nChar == 0:
|
||||
for nLine, aLine in enumerate(theLines, start=1):
|
||||
if len(aLine.strip()) == 0:
|
||||
continue
|
||||
|
||||
if aLine.startswith("#"):
|
||||
@@ -363,7 +361,7 @@ class NWIndex():
|
||||
else:
|
||||
return False
|
||||
|
||||
sTitle = "T%06d" % nLine
|
||||
sTitle = f"T{nLine:06d}"
|
||||
self._fileIndex[tHandle][sTitle] = {
|
||||
"level": hDepth,
|
||||
"title": hText,
|
||||
@@ -391,14 +389,13 @@ class NWIndex():
|
||||
"pCount": 0,
|
||||
"synopsis": "",
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
def _indexWordCounts(self, tHandle, theText, nTitle):
|
||||
"""Count text stats and save the counts to the index.
|
||||
"""
|
||||
cC, wC, pC = countWords(theText)
|
||||
sTitle = "T%06d" % nTitle
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
self._fileIndex[tHandle][sTitle]["cCount"] = cC
|
||||
@@ -409,7 +406,7 @@ class NWIndex():
|
||||
def _indexSynopsis(self, tHandle, theText, nTitle):
|
||||
"""Save the synopsis to the index.
|
||||
"""
|
||||
sTitle = "T%06d" % nTitle
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if tHandle in self._fileIndex:
|
||||
if sTitle in self._fileIndex[tHandle]:
|
||||
self._fileIndex[tHandle][sTitle]["synopsis"] = theText
|
||||
@@ -428,7 +425,7 @@ class NWIndex():
|
||||
logger.warning("Skipping invalid keyword '%s' in '%s'", theBits[0], tHandle)
|
||||
return
|
||||
|
||||
sTitle = "T%06d" % nTitle
|
||||
sTitle = f"T{nTitle:06d}"
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
self._tagIndex[theBits[1]] = [nLine, tHandle, itemClass.name, sTitle]
|
||||
|
||||
@@ -525,7 +522,7 @@ class NWIndex():
|
||||
"""
|
||||
for tHandle in self._listNovelHandles(skipExcluded):
|
||||
for sTitle in sorted(self._fileIndex[tHandle]):
|
||||
tKey = "%s:%s" % (tHandle, sTitle)
|
||||
tKey = f"{tHandle}:{sTitle}"
|
||||
yield tKey, tHandle, sTitle, self._fileIndex[tHandle][sTitle]
|
||||
|
||||
def getNovelWordCount(self, skipExcluded=True):
|
||||
@@ -559,7 +556,7 @@ class NWIndex():
|
||||
return theCounts
|
||||
|
||||
for sTitle, sData in hRecord.items():
|
||||
theCounts.append(("%s:%s" % (tHandle, sTitle), sData["wCount"]))
|
||||
theCounts.append((f"{tHandle}:{sTitle}", sData["wCount"]))
|
||||
|
||||
return theCounts
|
||||
|
||||
|
||||
@@ -103,11 +103,8 @@ class NWItem():
|
||||
logger.error("XML item entry does not have a handle")
|
||||
return False
|
||||
|
||||
if "parent" in xItem.attrib:
|
||||
self.setParent(xItem.attrib["parent"])
|
||||
|
||||
if "order" in xItem.attrib:
|
||||
self.setOrder(xItem.attrib["order"])
|
||||
self.setParent(xItem.attrib.get("parent", None))
|
||||
self.setOrder(xItem.attrib.get("order", 0))
|
||||
|
||||
tmpStatus = ""
|
||||
for xValue in xItem:
|
||||
@@ -200,11 +197,8 @@ class NWItem():
|
||||
def setHandle(self, theHandle):
|
||||
"""Set the item handle, and ensure it is valid.
|
||||
"""
|
||||
if isinstance(theHandle, str):
|
||||
if isHandle(theHandle):
|
||||
self.itemHandle = theHandle
|
||||
else:
|
||||
self.itemHandle = None
|
||||
if isHandle(theHandle):
|
||||
self.itemHandle = theHandle
|
||||
else:
|
||||
self.itemHandle = None
|
||||
return
|
||||
@@ -214,11 +208,8 @@ class NWItem():
|
||||
"""
|
||||
if theParent is None:
|
||||
self.itemParent = None
|
||||
elif isinstance(theParent, str):
|
||||
if isHandle(theParent):
|
||||
self.itemParent = theParent
|
||||
else:
|
||||
self.itemParent = None
|
||||
elif isHandle(theParent):
|
||||
self.itemParent = theParent
|
||||
else:
|
||||
self.itemParent = None
|
||||
return
|
||||
|
||||
+70
-130
@@ -30,88 +30,40 @@ import logging
|
||||
import novelwriter
|
||||
|
||||
from novelwriter.constants import nwFiles
|
||||
from novelwriter.common import checkBool, checkFloat, checkInt, checkString
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
VALID_MAP = {
|
||||
"GuiWritingStats": {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"widthCol3", "sortCol", "sortOrder", "incNovel", "incNotes",
|
||||
"hideZeros", "hideNegative", "groupByDay", "showIdleTime", "histMax"
|
||||
},
|
||||
"GuiDocSplit": {"spLevel"},
|
||||
"GuiBuildNovel": {
|
||||
"winWidth", "winHeight", "boxWidth", "docWidth", "addNovel",
|
||||
"addNotes", "ignoreFlag", "justifyText", "excludeBody", "textFont",
|
||||
"textSize", "lineHeight", "noStyling", "incSynopsis", "incComments",
|
||||
"incKeywords", "incBodyText", "replaceTabs", "replaceUCode"
|
||||
},
|
||||
"GuiOutline": {"headerOrder", "columnWidth", "columnHidden"},
|
||||
"GuiProjectSettings": {
|
||||
"winWidth", "winHeight", "replaceColW", "statusColW", "importColW"
|
||||
},
|
||||
"GuiProjectDetails": {
|
||||
"winWidth", "winHeight", "widthCol0", "widthCol1", "widthCol2",
|
||||
"widthCol3", "widthCol4", "wordsPerPage", "countFrom", "clearDouble"
|
||||
},
|
||||
"GuiWordList": {"winWidth", "winHeight"}
|
||||
}
|
||||
|
||||
|
||||
class OptionState():
|
||||
|
||||
def __init__(self, theProject):
|
||||
|
||||
self.theProject = theProject
|
||||
self.theState = {}
|
||||
self.validMap = {
|
||||
"GuiWritingStats": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"widthCol0",
|
||||
"widthCol1",
|
||||
"widthCol2",
|
||||
"widthCol3",
|
||||
"sortCol",
|
||||
"sortOrder",
|
||||
"incNovel",
|
||||
"incNotes",
|
||||
"hideZeros",
|
||||
"hideNegative",
|
||||
"groupByDay",
|
||||
"showIdleTime",
|
||||
"histMax",
|
||||
},
|
||||
"GuiDocSplit": {
|
||||
"spLevel",
|
||||
},
|
||||
"GuiBuildNovel": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"boxWidth",
|
||||
"docWidth",
|
||||
"addNovel",
|
||||
"addNotes",
|
||||
"ignoreFlag",
|
||||
"justifyText",
|
||||
"excludeBody",
|
||||
"textFont",
|
||||
"textSize",
|
||||
"lineHeight",
|
||||
"noStyling",
|
||||
"incSynopsis",
|
||||
"incComments",
|
||||
"incKeywords",
|
||||
"incBodyText",
|
||||
"replaceTabs",
|
||||
"replaceUCode",
|
||||
},
|
||||
"GuiOutline": {
|
||||
"headerOrder",
|
||||
"columnWidth",
|
||||
"columnHidden",
|
||||
},
|
||||
"GuiProjectSettings": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"replaceColW",
|
||||
"statusColW",
|
||||
"importColW",
|
||||
},
|
||||
"GuiProjectDetails": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
"widthCol0",
|
||||
"widthCol1",
|
||||
"widthCol2",
|
||||
"widthCol3",
|
||||
"widthCol4",
|
||||
"wordsPerPage",
|
||||
"countFrom",
|
||||
"clearDouble",
|
||||
},
|
||||
"GuiWordList": {
|
||||
"winWidth",
|
||||
"winHeight",
|
||||
}
|
||||
}
|
||||
|
||||
self._theState = {}
|
||||
return
|
||||
|
||||
##
|
||||
@@ -139,11 +91,11 @@ class OptionState():
|
||||
|
||||
# Filter out unused variables
|
||||
for aGroup in theState:
|
||||
if aGroup in self.validMap:
|
||||
self.theState[aGroup] = {}
|
||||
if aGroup in VALID_MAP:
|
||||
self._theState[aGroup] = {}
|
||||
for anOpt in theState[aGroup]:
|
||||
if anOpt in self.validMap[aGroup]:
|
||||
self.theState[aGroup][anOpt] = theState[aGroup][anOpt]
|
||||
if anOpt in VALID_MAP[aGroup]:
|
||||
self._theState[aGroup][anOpt] = theState[aGroup][anOpt]
|
||||
|
||||
return True
|
||||
|
||||
@@ -158,7 +110,7 @@ class OptionState():
|
||||
|
||||
try:
|
||||
with open(stateFile, mode="w+", encoding="utf-8") as outFile:
|
||||
json.dump(self.theState, outFile, indent=2)
|
||||
json.dump(self._theState, outFile, indent=2)
|
||||
except Exception:
|
||||
logger.error("Failed to save GUI options file")
|
||||
novelwriter.logException()
|
||||
@@ -170,21 +122,21 @@ class OptionState():
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setValue(self, setGroup, setName, setValue):
|
||||
def setValue(self, group, name, value):
|
||||
"""Saves a value, with a given group and name.
|
||||
"""
|
||||
if setGroup not in self.validMap:
|
||||
logger.error("Unknown option group '%s'", setGroup)
|
||||
if group not in VALID_MAP:
|
||||
logger.error("Unknown option group '%s'", group)
|
||||
return False
|
||||
|
||||
if setName not in self.validMap[setGroup]:
|
||||
logger.error("Unknown option name '%s'", setName)
|
||||
if name not in VALID_MAP[group]:
|
||||
logger.error("Unknown option name '%s'", name)
|
||||
return False
|
||||
|
||||
if setGroup not in self.theState:
|
||||
self.theState[setGroup] = {}
|
||||
if group not in self._theState:
|
||||
self._theState[group] = {}
|
||||
|
||||
self.theState[setGroup][setName] = setValue
|
||||
self._theState[group][name] = value
|
||||
|
||||
return True
|
||||
|
||||
@@ -192,79 +144,67 @@ class OptionState():
|
||||
# Getters
|
||||
##
|
||||
|
||||
def getValue(self, getGroup, getName, defaultValue):
|
||||
def getValue(self, group, name, default):
|
||||
"""Return an arbitrary type value, if it exists. Otherwise,
|
||||
return the default value.
|
||||
"""
|
||||
if getGroup in self.theState:
|
||||
if getName in self.theState[getGroup]:
|
||||
return self.theState[getGroup][getName]
|
||||
return defaultValue
|
||||
if group in self._theState:
|
||||
return self._theState[group].get(name, default)
|
||||
return default
|
||||
|
||||
def getString(self, getGroup, getName, defaultValue):
|
||||
def getString(self, group, name, default):
|
||||
"""Return the value as a string, if it exists. Otherwise, return
|
||||
the default value.
|
||||
"""
|
||||
if getGroup in self.theState:
|
||||
if getName in self.theState[getGroup]:
|
||||
return str(self.theState[getGroup][getName])
|
||||
return defaultValue
|
||||
if group in self._theState:
|
||||
return checkString(self._theState[group].get(name, default), default)
|
||||
return default
|
||||
|
||||
def getInt(self, getGroup, getName, defaultValue):
|
||||
def getInt(self, group, name, default):
|
||||
"""Return the value as an int, if it exists. Otherwise, return
|
||||
the default value.
|
||||
"""
|
||||
if getGroup in self.theState:
|
||||
if getName in self.theState[getGroup]:
|
||||
try:
|
||||
return int(self.theState[getGroup][getName])
|
||||
except Exception as e:
|
||||
logger.warning(str(e))
|
||||
return defaultValue
|
||||
return defaultValue
|
||||
if group in self._theState:
|
||||
return checkInt(self._theState[group].get(name, default), default)
|
||||
return default
|
||||
|
||||
def getFloat(self, getGroup, getName, defaultValue):
|
||||
def getFloat(self, group, name, default):
|
||||
"""Return the value as a float, if it exists. Otherwise, return
|
||||
the default value.
|
||||
"""
|
||||
if getGroup in self.theState:
|
||||
if getName in self.theState[getGroup]:
|
||||
try:
|
||||
return float(self.theState[getGroup][getName])
|
||||
except Exception as e:
|
||||
logger.warning(str(e))
|
||||
return defaultValue
|
||||
return defaultValue
|
||||
if group in self._theState:
|
||||
return checkFloat(self._theState[group].get(name, default), default)
|
||||
return default
|
||||
|
||||
def getBool(self, getGroup, getName, defaultValue):
|
||||
def getBool(self, group, name, default):
|
||||
"""Return the value as a bool, if it exists. Otherwise, return
|
||||
the default value.
|
||||
"""
|
||||
if getGroup in self.theState:
|
||||
if getName in self.theState[getGroup]:
|
||||
return bool(self.theState[getGroup][getName])
|
||||
return defaultValue
|
||||
if group in self._theState:
|
||||
if name in self._theState[group]:
|
||||
return checkBool(self._theState[group].get(name, default), default)
|
||||
return default
|
||||
|
||||
##
|
||||
# Validators
|
||||
##
|
||||
|
||||
def validIntRange(self, theValue, intA, intB, intDefault):
|
||||
def validIntRange(self, value, first, last, default):
|
||||
"""Check that an int is in a given range. If it isn't, return
|
||||
the default value.
|
||||
"""
|
||||
if isinstance(theValue, int):
|
||||
if theValue >= intA and theValue <= intB:
|
||||
return theValue
|
||||
return intDefault
|
||||
if isinstance(value, int):
|
||||
if value >= first and value <= last:
|
||||
return value
|
||||
return default
|
||||
|
||||
def validIntTuple(self, theValue, theTuple, intDefault):
|
||||
def validIntTuple(self, value, valid, default):
|
||||
"""Check that an int is an element of a tuple. If it isn't,
|
||||
return the default value.
|
||||
"""
|
||||
if isinstance(theValue, int):
|
||||
if theValue in theTuple:
|
||||
return theValue
|
||||
return intDefault
|
||||
if isinstance(value, int):
|
||||
if value in valid:
|
||||
return value
|
||||
return default
|
||||
|
||||
# END Class OptionState
|
||||
|
||||
@@ -125,6 +125,7 @@ class NWProject():
|
||||
if not self.projTree.checkRootUnique(rootClass):
|
||||
self.theParent.makeAlert(self.tr("Duplicate root item detected."), nwAlert.ERROR)
|
||||
return None
|
||||
|
||||
newItem = NWItem(self)
|
||||
newItem.setName(rootName)
|
||||
newItem.setType(nwItemType.ROOT)
|
||||
@@ -356,7 +357,7 @@ class NWProject():
|
||||
return True
|
||||
|
||||
def openProject(self, fileName, overrideLock=False):
|
||||
"""Open the project file provided, or if doesn't exist, assume
|
||||
"""Open the project file provided. If it doesn't exist, assume
|
||||
it is a folder and look for the file within it. If successful,
|
||||
parse the XML of the file and populate the project variables and
|
||||
build the tree of project items.
|
||||
@@ -469,8 +470,8 @@ class NWProject():
|
||||
# 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 notes. Introduced in version
|
||||
# 1.5.
|
||||
# novel documents and one for project notes. Introduced in
|
||||
# version 1.5.
|
||||
|
||||
if fileVersion not in ("1.0", "1.1", "1.2", "1.3"):
|
||||
self.theParent.makeAlert(self.tr(
|
||||
@@ -973,7 +974,7 @@ class NWProject():
|
||||
return False
|
||||
|
||||
self.bookAuthors = []
|
||||
for bookAuthor in bookAuthors.split("\n"):
|
||||
for bookAuthor in bookAuthors.splitlines():
|
||||
bookAuthor = bookAuthor.strip()
|
||||
if bookAuthor == "":
|
||||
continue
|
||||
@@ -1074,7 +1075,7 @@ class NWProject():
|
||||
replaceMap = self.statusItems.setNewEntries(newCols)
|
||||
for nwItem in self.projTree:
|
||||
if nwItem.itemClass == nwItemClass.NOVEL:
|
||||
if nwItem.itemStatus in replaceMap.keys():
|
||||
if nwItem.itemStatus in replaceMap:
|
||||
nwItem.setStatus(replaceMap[nwItem.itemStatus])
|
||||
self.setProjectChanged(True)
|
||||
return True
|
||||
@@ -1086,7 +1087,7 @@ class NWProject():
|
||||
replaceMap = self.importItems.setNewEntries(newCols)
|
||||
for nwItem in self.projTree:
|
||||
if nwItem.itemClass != nwItemClass.NOVEL:
|
||||
if nwItem.itemStatus in replaceMap.keys():
|
||||
if nwItem.itemStatus in replaceMap:
|
||||
nwItem.setStatus(replaceMap[nwItem.itemStatus])
|
||||
self.setProjectChanged(True)
|
||||
return True
|
||||
@@ -1104,7 +1105,7 @@ class NWProject():
|
||||
"""
|
||||
for valKey, valEntry in titleFormat.items():
|
||||
if valKey in self.titleFormat:
|
||||
self.titleFormat[valKey] = checkString(valEntry, self.titleFormat[valKey], False)
|
||||
self.titleFormat[valKey] = checkString(valEntry, self.titleFormat[valKey])
|
||||
return True
|
||||
|
||||
def setProjectChanged(self, bValue):
|
||||
@@ -1346,7 +1347,7 @@ class NWProject():
|
||||
return
|
||||
|
||||
def _packProjectKeyValue(self, xParent, theName, theDict):
|
||||
"""Pack the entries in the auto-replace dictionary.
|
||||
"""Pack the entries of a dictionary into an xml element.
|
||||
"""
|
||||
xAutoRep = etree.SubElement(xParent, theName)
|
||||
for aKey, aValue in theDict.items():
|
||||
|
||||
@@ -35,16 +35,24 @@ class NWSpellEnchant():
|
||||
def __init__(self):
|
||||
|
||||
self.mainConf = novelwriter.CONFIG
|
||||
self.theDict = None
|
||||
self.projDict = set()
|
||||
self.projectDict = None
|
||||
self.spellLanguage = None
|
||||
self.theBroker = None
|
||||
|
||||
self._theDict = None
|
||||
self._projDict = set()
|
||||
self._projectDict = None
|
||||
self._spellLanguage = None
|
||||
self._theBroker = None
|
||||
|
||||
logger.debug("Enchant spell checking activated")
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Getters and Setters
|
||||
##
|
||||
|
||||
def spellLanguage(self):
|
||||
return self._spellLanguage
|
||||
|
||||
def setLanguage(self, theLang, projectDict=None):
|
||||
"""Load a dictionary for the language specified in the config.
|
||||
If that fails, we load a mock dictionary so that lookups don't
|
||||
@@ -52,49 +60,53 @@ class NWSpellEnchant():
|
||||
"""
|
||||
try:
|
||||
import enchant
|
||||
if self.theBroker is not None:
|
||||
if self._theBroker is not None:
|
||||
logger.debug("Deleting old pyenchant broker")
|
||||
del self.theBroker
|
||||
del self._theBroker
|
||||
|
||||
self.theBroker = enchant.Broker()
|
||||
self.theDict = self.theBroker.request_dict(theLang)
|
||||
self.spellLanguage = theLang
|
||||
self._theBroker = enchant.Broker()
|
||||
self._theDict = self._theBroker.request_dict(theLang)
|
||||
self._spellLanguage = theLang
|
||||
logger.debug("Enchant spell checking for language '%s' loaded", theLang)
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load enchant spell checking for language '%s'", theLang)
|
||||
self.theDict = FakeEnchant()
|
||||
self.spellLanguage = None
|
||||
self._theDict = FakeEnchant()
|
||||
self._spellLanguage = None
|
||||
|
||||
self._readProjectDictionary(projectDict)
|
||||
for pWord in self.projDict:
|
||||
self.theDict.add_to_session(pWord)
|
||||
for pWord in self._projDict:
|
||||
self._theDict.add_to_session(pWord)
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Methods
|
||||
##
|
||||
|
||||
def checkWord(self, theWord):
|
||||
"""Wrapper function for pyenchant.
|
||||
"""
|
||||
return self.theDict.check(theWord)
|
||||
return self._theDict.check(theWord)
|
||||
|
||||
def suggestWords(self, theWord):
|
||||
"""Wrapper function for pyenchant.
|
||||
"""
|
||||
return self.theDict.suggest(theWord)
|
||||
return self._theDict.suggest(theWord)
|
||||
|
||||
def addWord(self, newWord):
|
||||
"""Add a word to the project dictionary.
|
||||
"""
|
||||
self.theDict.add_to_session(newWord)
|
||||
self._theDict.add_to_session(newWord)
|
||||
|
||||
if self.projectDict is not None and newWord not in self.projDict:
|
||||
if self._projectDict is not None and newWord not in self._projDict:
|
||||
newWord = newWord.strip()
|
||||
try:
|
||||
with open(self.projectDict, mode="a+", encoding="utf-8") as outFile:
|
||||
with open(self._projectDict, mode="a+", encoding="utf-8") as outFile:
|
||||
outFile.write("%s\n" % newWord)
|
||||
self.projDict.add(newWord)
|
||||
self._projDict.add(newWord)
|
||||
except Exception:
|
||||
logger.error("Failed to add word to project word list %s", str(self.projectDict))
|
||||
logger.error("Failed to add word to project word list %s", str(self._projectDict))
|
||||
novelwriter.logException()
|
||||
return False
|
||||
return True
|
||||
@@ -119,8 +131,8 @@ class NWSpellEnchant():
|
||||
dictionary.
|
||||
"""
|
||||
try:
|
||||
spTag = self.theDict.tag
|
||||
spName = self.theDict.provider.name
|
||||
spTag = self._theDict.tag
|
||||
spName = self._theDict.provider.name
|
||||
except Exception:
|
||||
logger.error("Failed to extract information about the dictionary")
|
||||
novelwriter.logException()
|
||||
@@ -137,8 +149,8 @@ class NWSpellEnchant():
|
||||
"""Read the content of the project dictionary, and add it to the
|
||||
lookup lists.
|
||||
"""
|
||||
self.projDict = set()
|
||||
self.projectDict = projectDict
|
||||
self._projDict = set()
|
||||
self._projectDict = projectDict
|
||||
|
||||
if projectDict is None:
|
||||
return False
|
||||
@@ -151,9 +163,9 @@ class NWSpellEnchant():
|
||||
with open(projectDict, mode="r", encoding="utf-8") as wordsFile:
|
||||
for theLine in wordsFile:
|
||||
theLine = theLine.strip()
|
||||
if len(theLine) > 0 and theLine not in self.projDict:
|
||||
self.projDict.add(theLine)
|
||||
logger.debug("Project word list contains %d words", len(self.projDict))
|
||||
if len(theLine) > 0 and theLine not in self._projDict:
|
||||
self._projDict.add(theLine)
|
||||
logger.debug("Project word list contains %d words", len(self._projDict))
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to load project word list")
|
||||
|
||||
+59
-63
@@ -109,7 +109,7 @@ class ToHtml(Tokenizer):
|
||||
to theResult.
|
||||
"""
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
htmlTags = { # HTML4 + CSS2
|
||||
htmlTags = { # HTML4 + CSS2 (for Qt)
|
||||
self.FMT_B_B: "<b>",
|
||||
self.FMT_B_E: "</b>",
|
||||
self.FMT_I_B: "<i>",
|
||||
@@ -118,7 +118,7 @@ class ToHtml(Tokenizer):
|
||||
self.FMT_D_E: "</span>",
|
||||
}
|
||||
else:
|
||||
htmlTags = { # HTML5
|
||||
htmlTags = { # HTML5 (for export)
|
||||
self.FMT_B_B: "<strong>",
|
||||
self.FMT_B_E: "</strong>",
|
||||
self.FMT_I_B: "<em>",
|
||||
@@ -176,17 +176,18 @@ class ToHtml(Tokenizer):
|
||||
aStyle.append("margin-top: 0;")
|
||||
|
||||
if tStyle & self.A_IND_L:
|
||||
aStyle.append("margin-left: %dpx;" % self.mainConf.tabWidth)
|
||||
aStyle.append(f"margin-left: {self.mainConf.tabWidth:d}px;")
|
||||
if tStyle & self.A_IND_R:
|
||||
aStyle.append("margin-right: %dpx;" % self.mainConf.tabWidth)
|
||||
aStyle.append(f"margin-right: {self.mainConf.tabWidth:d}px;")
|
||||
|
||||
if len(aStyle) > 0:
|
||||
hStyle = " style='%s'" % (" ".join(aStyle))
|
||||
stVals = " ".join(aStyle)
|
||||
hStyle = f" style='{stVals}'"
|
||||
else:
|
||||
hStyle = ""
|
||||
|
||||
if self.linkHeaders:
|
||||
aNm = "<a name='T%06d'></a>" % tLine
|
||||
aNm = f"<a name='T{tLine:06d}'></a>"
|
||||
else:
|
||||
aNm = ""
|
||||
|
||||
@@ -200,36 +201,36 @@ class ToHtml(Tokenizer):
|
||||
parClass = ""
|
||||
if len(thisPar) > 0:
|
||||
tTemp = "<br/>".join(thisPar)
|
||||
tmpResult.append("<p%s%s>%s</p>\n" % (parClass, parStyle, tTemp.rstrip()))
|
||||
tmpResult.append(f"<p{parClass+parStyle}>{tTemp.rstrip()}</p>\n")
|
||||
thisPar = []
|
||||
parStyle = None
|
||||
|
||||
elif tType == self.T_TITLE:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<h1 class='title'%s>%s%s</h1>\n" % (hStyle, aNm, tHead))
|
||||
tmpResult.append(f"<h1 class='title'{hStyle}>{aNm}{tHead}</h1>\n")
|
||||
|
||||
elif tType == self.T_UNNUM:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<%s%s>%s%s</%s>\n" % (h2, hStyle, aNm, tHead, h2))
|
||||
tmpResult.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
|
||||
|
||||
elif tType == self.T_HEAD1:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<%s%s%s>%s%s</%s>\n" % (h1, h1Cl, hStyle, aNm, tHead, h1))
|
||||
tmpResult.append(f"<{h1}{h1Cl}{hStyle}>{aNm}{tHead}</{h1}>\n")
|
||||
|
||||
elif tType == self.T_HEAD2:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<%s%s>%s%s</%s>\n" % (h2, hStyle, aNm, tHead, h2))
|
||||
tmpResult.append(f"<{h2}{hStyle}>{aNm}{tHead}</{h2}>\n")
|
||||
|
||||
elif tType == self.T_HEAD3:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<%s%s>%s%s</%s>\n" % (h3, hStyle, aNm, tHead, h3))
|
||||
tmpResult.append(f"<{h3}{hStyle}>{aNm}{tHead}</{h3}>\n")
|
||||
|
||||
elif tType == self.T_HEAD4:
|
||||
tHead = tText.replace(r"\\", "<br/>")
|
||||
tmpResult.append("<%s%s>%s%s</%s>\n" % (h4, hStyle, aNm, tHead, h4))
|
||||
tmpResult.append(f"<{h4}{hStyle}>{aNm}{tHead}</{h4}>\n")
|
||||
|
||||
elif tType == self.T_SEP:
|
||||
tmpResult.append("<p class='sep'>%s</p>\n" % tText)
|
||||
tmpResult.append(f"<p class='sep'>{tText}</p>\n")
|
||||
|
||||
elif tType == self.T_SKIP:
|
||||
tmpResult.append("<p class='skip'> </p>\n")
|
||||
@@ -249,7 +250,7 @@ class ToHtml(Tokenizer):
|
||||
tmpResult.append(self._formatComments(tText))
|
||||
|
||||
elif tType == self.T_KEYWORD and self.doKeywords:
|
||||
tTemp = "<p%s>%s</p>\n" % (hStyle, self._formatKeywords(tText))
|
||||
tTemp = f"<p{hStyle}>{self._formatKeywords(tText)}</p>\n"
|
||||
tmpResult.append(tTemp)
|
||||
|
||||
self.theResult = "".join(tmpResult)
|
||||
@@ -298,9 +299,9 @@ class ToHtml(Tokenizer):
|
||||
"""Replace tabs with spaces in the html.
|
||||
"""
|
||||
htmlText = []
|
||||
eightSpace = spaceChar*nSpaces
|
||||
tabSpace = spaceChar*nSpaces
|
||||
for aLine in self.fullHTML:
|
||||
htmlText.append(aLine.replace("\t", eightSpace))
|
||||
htmlText.append(aLine.replace("\t", tabSpace))
|
||||
|
||||
self.fullHTML = htmlText
|
||||
return
|
||||
@@ -315,75 +316,76 @@ class ToHtml(Tokenizer):
|
||||
mScale = self.lineHeight/1.15
|
||||
textAlign = "justify" if self.doJustify else "left"
|
||||
|
||||
theStyles.append("body {font-family: '%s'; font-size: %dpt;}" % (
|
||||
theStyles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format(
|
||||
self.textFont, self.textSize
|
||||
))
|
||||
theStyles.append((
|
||||
"p {"
|
||||
"text-align: %s; line-height: %d%%; "
|
||||
"margin-top: %.2fem; margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"p {{"
|
||||
"text-align: {0}; line-height: {1:d}%; "
|
||||
"margin-top: {2:.2f}em; margin-bottom: {3:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
textAlign,
|
||||
round(100 * self.lineHeight),
|
||||
mScale * self.marginText[0],
|
||||
mScale * self.marginText[1],
|
||||
))
|
||||
theStyles.append((
|
||||
"h1 {"
|
||||
"h1 {{"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale * self.marginHead1[0], mScale * self.marginHead1[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h2 {"
|
||||
"h2 {{"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale * self.marginHead2[0], mScale * self.marginHead2[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h3 {"
|
||||
"h3 {{"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale * self.marginHead3[0], mScale * self.marginHead3[1]
|
||||
))
|
||||
theStyles.append((
|
||||
"h4 {"
|
||||
"h4 {{"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale * self.marginHead4[0], mScale * self.marginHead4[1]
|
||||
))
|
||||
theStyles.append((
|
||||
".title {"
|
||||
".title {{"
|
||||
"font-size: 2.5em; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;"
|
||||
"}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale * self.marginTitle[0], mScale * self.marginTitle[1]
|
||||
))
|
||||
theStyles.append((
|
||||
".sep, .skip {"
|
||||
".sep, .skip {{"
|
||||
"text-align: center; "
|
||||
"margin-top: %.2fem; "
|
||||
"margin-bottom: %.2fem;}"
|
||||
) % (
|
||||
"margin-top: {0:.2f}em; "
|
||||
"margin-bottom: {1:.2f}em;"
|
||||
"}}"
|
||||
).format(
|
||||
mScale, mScale
|
||||
))
|
||||
|
||||
@@ -421,31 +423,25 @@ class ToHtml(Tokenizer):
|
||||
def _formatKeywords(self, tText):
|
||||
"""Apply HTML formatting to keywords.
|
||||
"""
|
||||
isValid, theBits, thePos = self.theParent.theIndex.scanThis("@"+tText)
|
||||
isValid, theBits, _ = self.theParent.theIndex.scanThis("@"+tText)
|
||||
if not isValid or not theBits:
|
||||
return ""
|
||||
|
||||
retText = ""
|
||||
refTags = []
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
retText += "<span class='tags'>%s:</span> " % nwLabels.KEY_NAME[theBits[0]]
|
||||
retText += f"<span class='tags'>{nwLabels.KEY_NAME[theBits[0]]}:</span> "
|
||||
if len(theBits) > 1:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
retText += "<a name='tag_%s'>%s</a>" % (
|
||||
theBits[1], theBits[1]
|
||||
)
|
||||
retText += f"<a name='tag_{theBits[1]}'>{theBits[1]}</a>"
|
||||
else:
|
||||
if self.genMode == self.M_PREVIEW:
|
||||
for tTag in theBits[1:]:
|
||||
refTags.append("<a href='#%s=%s'>%s</a>" % (
|
||||
theBits[0][1:], tTag, tTag
|
||||
))
|
||||
refTags.append(f"<a href='#{theBits[0][1:]}={tTag}'>{tTag}</a>")
|
||||
retText += ", ".join(refTags)
|
||||
else:
|
||||
for tTag in theBits[1:]:
|
||||
refTags.append("<a href='#tag_%s'>%s</a>" % (
|
||||
tTag, tTag
|
||||
))
|
||||
refTags.append(f"<a href='#tag_{tTag}'>{tTag}</a>")
|
||||
retText += ", ".join(refTags)
|
||||
|
||||
return retText
|
||||
|
||||
@@ -267,13 +267,14 @@ class Tokenizer():
|
||||
else:
|
||||
textAlign = self.A_PBB | self.A_CENTRE
|
||||
|
||||
theTitle = "%s: %s" % (self._localLookup("Notes"), theItem.itemName)
|
||||
locNotes = self._localLookup("Notes")
|
||||
theTitle = f"{locNotes}: {theItem.itemName}"
|
||||
self.theTokens = []
|
||||
self.theTokens.append((
|
||||
self.T_TITLE, 0, theTitle, None, textAlign
|
||||
))
|
||||
if self.keepMarkdown:
|
||||
self.theMarkdown.append("# %s\n\n" % theTitle)
|
||||
self.theMarkdown.append(f"# {theTitle}\n\n")
|
||||
|
||||
return True
|
||||
|
||||
@@ -302,7 +303,7 @@ class Tokenizer():
|
||||
errVal = self.tr("Document '{0}' is too big ({1} MB). Skipping.").format(
|
||||
self.theItem.itemName, f"{docSize/1.0e6:.2f}"
|
||||
)
|
||||
self.theText = "# %s\n\n%s\n\n" % (self.tr("ERROR"), 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
|
||||
@@ -318,7 +319,7 @@ class Tokenizer():
|
||||
if len(self.theProject.autoReplace) > 0:
|
||||
repDict = {}
|
||||
for aKey, aVal in self.theProject.autoReplace.items():
|
||||
repDict["<%s>" % aKey] = aVal
|
||||
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)
|
||||
|
||||
|
||||
+13
-11
@@ -100,33 +100,33 @@ class ToMarkdown(Tokenizer):
|
||||
# Process Text Type
|
||||
if tType == self.T_EMPTY:
|
||||
if len(thisPar) > 0:
|
||||
tTemp = " \n".join(thisPar)
|
||||
tmpResult.append("%s\n\n" % tTemp.rstrip(" "))
|
||||
tTemp = (" \n".join(thisPar)).rstrip(" ")
|
||||
tmpResult.append(f"{tTemp}\n\n")
|
||||
thisPar = []
|
||||
|
||||
elif tType == self.T_TITLE:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("# %s\n\n" % tHead)
|
||||
tmpResult.append(f"# {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_UNNUM:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("## %s\n\n" % tHead)
|
||||
tmpResult.append(f"## {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_HEAD1:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("# %s\n\n" % tHead)
|
||||
tmpResult.append(f"# {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_HEAD2:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("## %s\n\n" % tHead)
|
||||
tmpResult.append(f"## {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_HEAD3:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("### %s\n\n" % tHead)
|
||||
tmpResult.append(f"### {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_HEAD4:
|
||||
tHead = tText.replace(r"\\", "\n")
|
||||
tmpResult.append("#### %s\n\n" % tHead)
|
||||
tmpResult.append(f"#### {tHead}\n\n")
|
||||
|
||||
elif tType == self.T_SEP:
|
||||
tmpResult.append("%s\n\n" % tText)
|
||||
@@ -141,10 +141,12 @@ class ToMarkdown(Tokenizer):
|
||||
thisPar.append(tTemp.rstrip())
|
||||
|
||||
elif tType == self.T_SYNOPSIS and self.doSynopsis:
|
||||
tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Synopsis"), tText))
|
||||
locName = self._localLookup("Synopsis")
|
||||
tmpResult.append(f"**{locName}:** {tText}\n\n")
|
||||
|
||||
elif tType == self.T_COMMENT and self.doComments:
|
||||
tmpResult.append("**%s:** %s\n\n" % (self._localLookup("Comment"), tText))
|
||||
locName = self._localLookup("Comment")
|
||||
tmpResult.append(f"**{locName}:** {tText}\n\n")
|
||||
|
||||
elif tType == self.T_KEYWORD and self.doKeywords:
|
||||
tmpResult.append(self._formatKeywords(tText, tStyle))
|
||||
@@ -189,7 +191,7 @@ class ToMarkdown(Tokenizer):
|
||||
|
||||
retText = ""
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
retText += "**%s:** " % nwLabels.KEY_NAME[theBits[0]]
|
||||
retText += f"**{nwLabels.KEY_NAME[theBits[0]]}:** "
|
||||
|
||||
if len(theBits) > 1:
|
||||
retText += ", ".join(theBits[1:])
|
||||
|
||||
+40
-37
@@ -45,18 +45,35 @@ XML_NS = {
|
||||
"meta": "urn:oasis:names:tc:opendocument:xmlns:meta:1.0",
|
||||
"fo": "urn:oasis:names:tc:opendocument:xmlns:xsl-fo-compatible:1.0",
|
||||
}
|
||||
MANI_NS = {
|
||||
"manifest": "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"
|
||||
}
|
||||
OFFICE_NS = {
|
||||
"office": XML_NS["office"]
|
||||
}
|
||||
|
||||
|
||||
def _mkTag(nsName, tagName, nsMap=XML_NS):
|
||||
"""Assemble namespace and tag name.
|
||||
"""
|
||||
theNS = nsMap.get(nsName, "")
|
||||
if theNS:
|
||||
return f"{{{theNS}}}{tagName}"
|
||||
logger.warning("Missing xml namespace '%s'", nsName)
|
||||
return tagName
|
||||
|
||||
|
||||
# Mimetype and Version
|
||||
X_MIME = "application/vnd.oasis.opendocument.text"
|
||||
X_VERS = "1.2"
|
||||
|
||||
# Text Formatting Tags
|
||||
TAG_BR = "{%s}line-break" % XML_NS["text"]
|
||||
TAG_SPC = "{%s}s" % XML_NS["text"]
|
||||
TAG_NSPC = "{%s}c" % XML_NS["text"]
|
||||
TAG_TAB = "{%s}tab" % XML_NS["text"]
|
||||
TAG_SPAN = "{%s}span" % XML_NS["text"]
|
||||
TAG_STNM = "{%s}style-name" % XML_NS["text"]
|
||||
TAG_BR = _mkTag("text", "line-break")
|
||||
TAG_SPC = _mkTag("text", "s")
|
||||
TAG_NSPC = _mkTag("text", "c")
|
||||
TAG_TAB = _mkTag("text", "tab")
|
||||
TAG_SPAN = _mkTag("text", "span")
|
||||
TAG_STNM = _mkTag("text", "style-name")
|
||||
|
||||
# Formatting Codes
|
||||
X_BLD = 0x01 # Bold format
|
||||
@@ -313,7 +330,7 @@ class ToOdt(Tokenizer):
|
||||
|
||||
# Meta Data
|
||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "creation-date"))
|
||||
xMeta.text = datetime.now().strftime("%Y-%m-%dT%H:%M:%S")
|
||||
xMeta.text = datetime.now().strftime(r"%Y-%m-%dT%H:%M:%S")
|
||||
|
||||
xMeta = etree.SubElement(self._xMeta, _mkTag("meta", "generator"))
|
||||
xMeta.text = f"novelWriter/{novelwriter.__version__}"
|
||||
@@ -472,24 +489,22 @@ class ToOdt(Tokenizer):
|
||||
def saveOpenDocText(self, savePath):
|
||||
"""Save the data to an .odt file.
|
||||
"""
|
||||
mMap = {"manifest": "urn:oasis:names:tc:opendocument:xmlns:manifest:1.0"}
|
||||
mMani = "{%s}manifest" % mMap["manifest"]
|
||||
mVers = "{%s}version" % mMap["manifest"]
|
||||
mPath = "{%s}full-path" % mMap["manifest"]
|
||||
mType = "{%s}media-type" % mMap["manifest"]
|
||||
mFile = "{%s}file-entry" % mMap["manifest"]
|
||||
mMani = _mkTag("manifest", "manifest", nsMap=MANI_NS)
|
||||
mVers = _mkTag("manifest", "version", nsMap=MANI_NS)
|
||||
mPath = _mkTag("manifest", "full-path", nsMap=MANI_NS)
|
||||
mType = _mkTag("manifest", "media-type", nsMap=MANI_NS)
|
||||
mFile = _mkTag("manifest", "file-entry", nsMap=MANI_NS)
|
||||
|
||||
xMani = etree.Element(mMani, attrib={mVers: X_VERS}, nsmap=mMap)
|
||||
xMani = etree.Element(mMani, attrib={mVers: X_VERS}, nsmap=MANI_NS)
|
||||
etree.SubElement(xMani, mFile, attrib={mPath: "/", mVers: X_VERS, mType: X_MIME})
|
||||
etree.SubElement(xMani, mFile, attrib={mPath: "settings.xml", mType: "text/xml"})
|
||||
etree.SubElement(xMani, mFile, attrib={mPath: "content.xml", mType: "text/xml"})
|
||||
etree.SubElement(xMani, mFile, attrib={mPath: "meta.xml", mType: "text/xml"})
|
||||
etree.SubElement(xMani, mFile, attrib={mPath: "styles.xml", mType: "text/xml"})
|
||||
|
||||
sMap = {"office": "urn:oasis:names:tc:opendocument:xmlns:office:1.0"}
|
||||
oRoot = "{%s}document-settings" % sMap["office"]
|
||||
oSett = "{%s}settings" % sMap["office"]
|
||||
xSett = etree.Element(oRoot, nsmap=sMap)
|
||||
oRoot = _mkTag("office", "document-settings", nsMap=OFFICE_NS)
|
||||
oSett = _mkTag("office", "settings", nsMap=OFFICE_NS)
|
||||
xSett = etree.Element(oRoot, nsmap=OFFICE_NS)
|
||||
etree.SubElement(xSett, oSett)
|
||||
|
||||
with ZipFile(savePath, mode="w") as outFile:
|
||||
@@ -520,16 +535,16 @@ class ToOdt(Tokenizer):
|
||||
"""Apply formatting to synopsis lines.
|
||||
"""
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
rTxt = "**%s:** %s" % (sSynop, tText)
|
||||
rFmt = "_B%s b_ %s" % (" "*len(sSynop), " "*len(tText))
|
||||
rTxt = "**{0}:** {1}".format(sSynop, tText)
|
||||
rFmt = "_B{0} b_ {1}".format(" "*len(sSynop), " "*len(tText))
|
||||
return rTxt, rFmt
|
||||
|
||||
def _formatComments(self, tText):
|
||||
"""Apply formatting to comments.
|
||||
"""
|
||||
sComm = self._localLookup("Comment")
|
||||
rTxt = "**%s:** %s" % (sComm, tText)
|
||||
rFmt = "_B%s b_ %s" % (" "*len(sComm), " "*len(tText))
|
||||
rTxt = "**{0}:** {1}".format(sComm, tText)
|
||||
rFmt = "_B{0} b_ {1}".format(" "*len(sComm), " "*len(tText))
|
||||
return rTxt, rFmt
|
||||
|
||||
def _formatKeywords(self, tText):
|
||||
@@ -543,8 +558,8 @@ class ToOdt(Tokenizer):
|
||||
rFmt = ""
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
tText = nwLabels.KEY_NAME[theBits[0]]
|
||||
rTxt += "**%s:** " % tText
|
||||
rFmt += "_B%s b_ " % (" "*len(tText))
|
||||
rTxt += "**{0}:** ".format(tText)
|
||||
rFmt += "_B{0} b_ ".format(" "*len(tText))
|
||||
if len(theBits) > 1:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
rTxt += theBits[1]
|
||||
@@ -1468,16 +1483,4 @@ class XMLParagraph():
|
||||
|
||||
return
|
||||
|
||||
|
||||
# =============================================================================================== #
|
||||
# Local Functions
|
||||
# =============================================================================================== #
|
||||
|
||||
def _mkTag(nsName, tagName):
|
||||
"""Assemble namespace and tag name.
|
||||
"""
|
||||
theNS = XML_NS.get(nsName, "")
|
||||
if theNS:
|
||||
return "{%s}%s" % (theNS, tagName)
|
||||
logger.warning("Missing xml namespace '%s'", nsName)
|
||||
return tagName
|
||||
# END Class XMLParagraph
|
||||
|
||||
@@ -158,7 +158,7 @@ class NWTree():
|
||||
continue
|
||||
tFile = tHandle+".nwd"
|
||||
if os.path.isfile(os.path.join(self.theProject.projContent, tFile)):
|
||||
tocLine = "%-25s %-9s %-8s %s" % (
|
||||
tocLine = "{0:<25s} {1:<9s} {2:<8s} {3:s}".format(
|
||||
os.path.join("content", tFile),
|
||||
tItem.itemClass.name,
|
||||
tItem.itemLayout.name,
|
||||
@@ -175,7 +175,7 @@ class NWTree():
|
||||
outFile.write("Table of Contents\n")
|
||||
outFile.write("=================\n")
|
||||
outFile.write("\n")
|
||||
outFile.write("%-25s %-9s %-8s %s\n" % (
|
||||
outFile.write("{0:<25s} {1:<9s} {2:<8s} {3:s}\n".format(
|
||||
"File Name", "Class", "Layout", "Document Label"
|
||||
))
|
||||
outFile.write("-"*max(tocLen, 62) + "\n")
|
||||
@@ -285,12 +285,12 @@ class NWTree():
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is not None:
|
||||
tTree.append(tHandle)
|
||||
for i in range(nwConst.MAX_DEPTH + 1):
|
||||
for _ in range(nwConst.MAX_DEPTH + 1):
|
||||
if tItem.itemParent is None:
|
||||
return tTree
|
||||
else:
|
||||
tHandle = tItem.itemParent
|
||||
tItem = self.__getitem__(tHandle)
|
||||
tItem = self.__getitem__(tHandle)
|
||||
if tItem is None:
|
||||
return tTree
|
||||
else:
|
||||
@@ -341,7 +341,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
|
||||
|
||||
Reference in New Issue
Block a user