Add annotations to tokenizer, html and markdown classes
This commit is contained in:
+60
-63
@@ -22,11 +22,15 @@ General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter import CONFIG
|
||||
from novelwriter.constants import nwKeyWords, nwLabels, nwHtmlUnicode
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.tokenizer import Tokenizer, stripEscape
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -38,8 +42,8 @@ class ToHtml(Tokenizer):
|
||||
M_EXPORT = 1 # Tweak output for saving to HTML or printing
|
||||
M_EBOOK = 2 # Tweak output for converting to epub
|
||||
|
||||
def __init__(self, theProject):
|
||||
super().__init__(theProject)
|
||||
def __init__(self, project: NWProject):
|
||||
super().__init__(project)
|
||||
|
||||
self._genMode = self.M_EXPORT
|
||||
self._cssStyles = True
|
||||
@@ -63,7 +67,7 @@ class ToHtml(Tokenizer):
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setPreview(self, doComments, doSynopsis):
|
||||
def setPreview(self, doComments: bool, doSynopsis: bool):
|
||||
"""If we're using this class to generate markdown preview, we
|
||||
need to make a few changes to formatting, which is managed by
|
||||
these flags.
|
||||
@@ -74,14 +78,14 @@ class ToHtml(Tokenizer):
|
||||
self._doSynopsis = doSynopsis
|
||||
return
|
||||
|
||||
def setStyles(self, cssStyles):
|
||||
"""Enable/disable CSS styling. Some elements may still have
|
||||
def setStyles(self, cssStyles: bool):
|
||||
"""Enable or disable CSS styling. Some elements may still have
|
||||
class tags.
|
||||
"""
|
||||
self._cssStyles = cssStyles
|
||||
return
|
||||
|
||||
def setReplaceUnicode(self, doReplace):
|
||||
def setReplaceUnicode(self, doReplace: bool):
|
||||
"""Set the translation map to either minimal or full unicode for
|
||||
html entities replacement.
|
||||
"""
|
||||
@@ -92,16 +96,14 @@ class ToHtml(Tokenizer):
|
||||
if doReplace:
|
||||
# Extend to all relevant Unicode characters
|
||||
self._trMap.update(str.maketrans(nwHtmlUnicode.U_TO_H))
|
||||
|
||||
return
|
||||
|
||||
##
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def getFullResultSize(self):
|
||||
"""Return the size of the full HTML result.
|
||||
"""
|
||||
def getFullResultSize(self) -> int:
|
||||
"""Return the size of the full HTML result."""
|
||||
return sum([len(x) for x in self._fullHTML])
|
||||
|
||||
def doPreProcessing(self):
|
||||
@@ -114,7 +116,7 @@ class ToHtml(Tokenizer):
|
||||
|
||||
def doConvert(self):
|
||||
"""Convert the list of text tokens into a HTML document saved
|
||||
to theResult.
|
||||
to _result.
|
||||
"""
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
htmlTags = { # HTML4 + CSS2 (for Qt)
|
||||
@@ -290,7 +292,7 @@ class ToHtml(Tokenizer):
|
||||
|
||||
return
|
||||
|
||||
def saveHTML5(self, savePath):
|
||||
def saveHTML5(self, savePath: str | Path):
|
||||
"""Save the data to an .html file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
@@ -324,9 +326,8 @@ class ToHtml(Tokenizer):
|
||||
|
||||
return
|
||||
|
||||
def replaceTabs(self, nSpaces=8, spaceChar=" "):
|
||||
"""Replace tabs with spaces in the html.
|
||||
"""
|
||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
||||
"""Replace tabs with spaces in the html."""
|
||||
htmlText = []
|
||||
tabSpace = spaceChar*nSpaces
|
||||
for aLine in self._fullHTML:
|
||||
@@ -335,20 +336,19 @@ class ToHtml(Tokenizer):
|
||||
self._fullHTML = htmlText
|
||||
return
|
||||
|
||||
def getStyleSheet(self):
|
||||
"""Generate a stylesheet appropriate for the current settings.
|
||||
"""
|
||||
theStyles = []
|
||||
def getStyleSheet(self) -> list:
|
||||
"""Generate a stylesheet for the current settings."""
|
||||
styles = []
|
||||
if not self._cssStyles:
|
||||
return theStyles
|
||||
return styles
|
||||
|
||||
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(
|
||||
styles.append("body {{font-family: '{0:s}'; font-size: {1:d}pt;}}".format(
|
||||
self._textFont, self._textSize
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
"p {{"
|
||||
"text-align: {0}; line-height: {1:d}%; "
|
||||
"margin-top: {2:.2f}em; margin-bottom: {3:.2f}em;"
|
||||
@@ -359,7 +359,7 @@ class ToHtml(Tokenizer):
|
||||
mScale * self._marginText[0],
|
||||
mScale * self._marginText[1],
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
"h1 {{"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
@@ -369,7 +369,7 @@ class ToHtml(Tokenizer):
|
||||
).format(
|
||||
mScale * self._marginHead1[0], mScale * self._marginHead1[1]
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
"h2 {{"
|
||||
"color: rgb(66, 113, 174); "
|
||||
"page-break-after: avoid; "
|
||||
@@ -379,7 +379,7 @@ class ToHtml(Tokenizer):
|
||||
).format(
|
||||
mScale * self._marginHead2[0], mScale * self._marginHead2[1]
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
"h3 {{"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
@@ -389,7 +389,7 @@ class ToHtml(Tokenizer):
|
||||
).format(
|
||||
mScale * self._marginHead3[0], mScale * self._marginHead3[1]
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
"h4 {{"
|
||||
"color: rgb(50, 50, 50); "
|
||||
"page-break-after: avoid; "
|
||||
@@ -399,7 +399,7 @@ class ToHtml(Tokenizer):
|
||||
).format(
|
||||
mScale * self._marginHead4[0], mScale * self._marginHead4[1]
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
".title {{"
|
||||
"font-size: 2.5em; "
|
||||
"margin-top: {0:.2f}em; "
|
||||
@@ -408,7 +408,7 @@ class ToHtml(Tokenizer):
|
||||
).format(
|
||||
mScale * self._marginTitle[0], mScale * self._marginTitle[1]
|
||||
))
|
||||
theStyles.append((
|
||||
styles.append((
|
||||
".sep, .skip {{"
|
||||
"text-align: center; "
|
||||
"margin-top: {0:.2f}em; "
|
||||
@@ -418,61 +418,58 @@ class ToHtml(Tokenizer):
|
||||
mScale, mScale
|
||||
))
|
||||
|
||||
theStyles.append("a {color: rgb(66, 113, 174);}")
|
||||
theStyles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
theStyles.append(".break {text-align: left;}")
|
||||
theStyles.append(".synopsis {font-style: italic;}")
|
||||
theStyles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||
styles.append("a {color: rgb(66, 113, 174);}")
|
||||
styles.append(".tags {color: rgb(245, 135, 31); font-weight: bold;}")
|
||||
styles.append(".break {text-align: left;}")
|
||||
styles.append(".synopsis {font-style: italic;}")
|
||||
styles.append(".comment {font-style: italic; color: rgb(100, 100, 100);}")
|
||||
|
||||
return theStyles
|
||||
return styles
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _formatSynopsis(self, tText):
|
||||
"""Apply HTML formatting to synopsis.
|
||||
"""
|
||||
def _formatSynopsis(self, text: str) -> str:
|
||||
"""Apply HTML formatting to synopsis."""
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
sSynop = self._trSynopsis
|
||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {tText}</p>\n"
|
||||
return f"<p class='comment'><span class='synopsis'>{sSynop}:</span> {text}</p>\n"
|
||||
else:
|
||||
sSynop = self._localLookup("Synopsis")
|
||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {tText}</p>\n"
|
||||
return f"<p class='synopsis'><strong>{sSynop}:</strong> {text}</p>\n"
|
||||
|
||||
def _formatComments(self, tText):
|
||||
"""Apply HTML formatting to comments.
|
||||
"""
|
||||
def _formatComments(self, text: str) -> str:
|
||||
"""Apply HTML formatting to comments."""
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
return f"<p class='comment'>{tText}</p>\n"
|
||||
return f"<p class='comment'>{text}</p>\n"
|
||||
else:
|
||||
sComm = self._localLookup("Comment")
|
||||
return f"<p class='comment'><strong>{sComm}:</strong> {tText}</p>\n"
|
||||
return f"<p class='comment'><strong>{sComm}:</strong> {text}</p>\n"
|
||||
|
||||
def _formatKeywords(self, tText):
|
||||
"""Apply HTML formatting to keywords.
|
||||
"""
|
||||
isValid, theBits, _ = self._project.index.scanThis("@"+tText)
|
||||
if not isValid or not theBits:
|
||||
def _formatKeywords(self, text: str) -> str:
|
||||
"""Apply HTML formatting to keywords."""
|
||||
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||
if not valid or not bits:
|
||||
return ""
|
||||
|
||||
retText = ""
|
||||
refTags = []
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
retText += f"<span class='tags'>{nwLabels.KEY_NAME[theBits[0]]}:</span> "
|
||||
if len(theBits) > 1:
|
||||
if theBits[0] == nwKeyWords.TAG_KEY:
|
||||
retText += f"<a name='tag_{theBits[1]}'>{theBits[1]}</a>"
|
||||
result = ""
|
||||
tags = []
|
||||
if bits[0] in nwLabels.KEY_NAME:
|
||||
result += f"<span class='tags'>{nwLabels.KEY_NAME[bits[0]]}:</span> "
|
||||
if len(bits) > 1:
|
||||
if bits[0] == nwKeyWords.TAG_KEY:
|
||||
result += f"<a name='tag_{bits[1]}'>{bits[1]}</a>"
|
||||
else:
|
||||
if self._genMode == self.M_PREVIEW:
|
||||
for tTag in theBits[1:]:
|
||||
refTags.append(f"<a href='#{theBits[0][1:]}={tTag}'>{tTag}</a>")
|
||||
retText += ", ".join(refTags)
|
||||
for tTag in bits[1:]:
|
||||
tags.append(f"<a href='#{bits[0][1:]}={tTag}'>{tTag}</a>")
|
||||
result += ", ".join(tags)
|
||||
else:
|
||||
for tTag in theBits[1:]:
|
||||
refTags.append(f"<a href='#tag_{tTag}'>{tTag}</a>")
|
||||
retText += ", ".join(refTags)
|
||||
for tTag in bits[1:]:
|
||||
tags.append(f"<a href='#tag_{tTag}'>{tTag}</a>")
|
||||
result += ", ".join(tags)
|
||||
|
||||
return retText
|
||||
return result
|
||||
|
||||
# END Class ToHtml
|
||||
|
||||
@@ -29,6 +29,7 @@ import re
|
||||
import logging
|
||||
|
||||
from abc import ABC, abstractmethod
|
||||
from pathlib import Path
|
||||
from operator import itemgetter
|
||||
from functools import partial
|
||||
|
||||
@@ -102,7 +103,7 @@ class Tokenizer(ABC):
|
||||
self._result = "" # 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._allMarkdown = [] # The result novelWriter markdown of all documents
|
||||
|
||||
# User Settings
|
||||
self._textFont = "Serif" # Output text font
|
||||
@@ -164,111 +165,136 @@ class Tokenizer(ABC):
|
||||
##
|
||||
|
||||
@property
|
||||
def theResult(self):
|
||||
def theResult(self) -> str:
|
||||
"""The result of the build process."""
|
||||
return self._result
|
||||
|
||||
@property
|
||||
def theMarkdown(self):
|
||||
return self._theMarkdown
|
||||
def theMarkdown(self) -> list:
|
||||
"""The combined novelWriter Markdown text."""
|
||||
return self._allMarkdown
|
||||
|
||||
@property
|
||||
def errData(self):
|
||||
def errData(self) -> list:
|
||||
"""The error data."""
|
||||
return self._errData
|
||||
|
||||
##
|
||||
# Setters
|
||||
##
|
||||
|
||||
def setTitleFormat(self, hFormat):
|
||||
def setTitleFormat(self, hFormat: str):
|
||||
"""Set the title format pattern."""
|
||||
self._fmtTitle = hFormat.strip()
|
||||
return
|
||||
|
||||
def setChapterFormat(self, hFormat):
|
||||
def setChapterFormat(self, hFormat: str):
|
||||
"""Set the chapert format pattern."""
|
||||
self._fmtChapter = hFormat.strip()
|
||||
return
|
||||
|
||||
def setUnNumberedFormat(self, hFormat):
|
||||
def setUnNumberedFormat(self, hFormat: str):
|
||||
"""Set the unnumbered format pattern."""
|
||||
self._fmtUnNum = hFormat.strip()
|
||||
return
|
||||
|
||||
def setSceneFormat(self, hFormat, hide):
|
||||
def setSceneFormat(self, hFormat: str, hide: bool):
|
||||
"""Set the scene format pattern and hidden status."""
|
||||
self._fmtScene = hFormat.strip()
|
||||
self._hideScene = hide
|
||||
return
|
||||
|
||||
def setSectionFormat(self, hFormat, hide):
|
||||
def setSectionFormat(self, hFormat: str, hide: bool):
|
||||
"""Set the section format pattern and hidden status."""
|
||||
self._fmtSection = hFormat.strip()
|
||||
self._hideSection = hide
|
||||
return
|
||||
|
||||
def setFont(self, textFont, textSize, textFixed=False):
|
||||
self._textFont = textFont
|
||||
self._textSize = round(int(textSize))
|
||||
self._textFixed = textFixed
|
||||
def setFont(self, family: str, size: int, isFixed: bool = False):
|
||||
"""Set the build font."""
|
||||
self._textFont = family
|
||||
self._textSize = round(int(size))
|
||||
self._textFixed = isFixed
|
||||
return
|
||||
|
||||
def setLineHeight(self, lineHeight):
|
||||
self._lineHeight = min(max(float(lineHeight), 0.5), 5.0)
|
||||
def setLineHeight(self, height: float):
|
||||
"""Set the line height between 0.5 and 5.0."""
|
||||
self._lineHeight = min(max(float(height), 0.5), 5.0)
|
||||
return
|
||||
|
||||
def setBlockIndent(self, blockIndent):
|
||||
self._blockIndent = min(max(float(blockIndent), 0.0), 10.0)
|
||||
def setBlockIndent(self, indent: float):
|
||||
"""Set the block indent between 0.0 and 10.0."""
|
||||
self._blockIndent = min(max(float(indent), 0.0), 10.0)
|
||||
return
|
||||
|
||||
def setJustify(self, doJustify):
|
||||
self._doJustify = doJustify
|
||||
def setJustify(self, state: bool):
|
||||
"""Enable or disable text justification."""
|
||||
self._doJustify = state
|
||||
return
|
||||
|
||||
def setTitleMargins(self, mUpper, mLower):
|
||||
self._marginTitle = (float(mUpper), float(mLower))
|
||||
def setTitleMargins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower title margin."""
|
||||
self._marginTitle = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setHead1Margins(self, mUpper, mLower):
|
||||
self._marginHead1 = (float(mUpper), float(mLower))
|
||||
def setHead1Margins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower header 1 margin."""
|
||||
self._marginHead1 = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setHead2Margins(self, mUpper, mLower):
|
||||
self._marginHead2 = (float(mUpper), float(mLower))
|
||||
def setHead2Margins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower header 2 margin."""
|
||||
self._marginHead2 = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setHead3Margins(self, mUpper, mLower):
|
||||
self._marginHead3 = (float(mUpper), float(mLower))
|
||||
def setHead3Margins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower header 3 margin."""
|
||||
self._marginHead3 = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setHead4Margins(self, mUpper, mLower):
|
||||
self._marginHead4 = (float(mUpper), float(mLower))
|
||||
def setHead4Margins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower header 4 margin."""
|
||||
self._marginHead4 = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setTextMargins(self, mUpper, mLower):
|
||||
self._marginText = (float(mUpper), float(mLower))
|
||||
def setTextMargins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower text margin."""
|
||||
self._marginText = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setMetaMargins(self, mUpper, mLower):
|
||||
self._marginMeta = (float(mUpper), float(mLower))
|
||||
def setMetaMargins(self, upper: float, lower: float):
|
||||
"""Set the upper and lower meta text margin."""
|
||||
self._marginMeta = (float(upper), float(lower))
|
||||
return
|
||||
|
||||
def setLinkHeaders(self, linkHeaders):
|
||||
self._linkHeaders = linkHeaders
|
||||
def setLinkHeaders(self, state: bool):
|
||||
"""Enable or disable adding an anchor before headers."""
|
||||
self._linkHeaders = state
|
||||
return
|
||||
|
||||
def setBodyText(self, doBodyText):
|
||||
self._doBodyText = doBodyText
|
||||
def setBodyText(self, state: bool):
|
||||
"""Include body text in build."""
|
||||
self._doBodyText = state
|
||||
return
|
||||
|
||||
def setSynopsis(self, doSynopsis):
|
||||
self._doSynopsis = doSynopsis
|
||||
def setSynopsis(self, state: bool):
|
||||
"""Include synopsis comments in build."""
|
||||
self._doSynopsis = state
|
||||
return
|
||||
|
||||
def setComments(self, doComments):
|
||||
self._doComments = doComments
|
||||
def setComments(self, state: bool):
|
||||
"""Include comments in build."""
|
||||
self._doComments = state
|
||||
return
|
||||
|
||||
def setKeywords(self, doKeywords):
|
||||
self._doKeywords = doKeywords
|
||||
def setKeywords(self, state: bool):
|
||||
"""Include keywords in build."""
|
||||
self._doKeywords = state
|
||||
return
|
||||
|
||||
def setKeepMarkdown(self, keepMarkdown):
|
||||
self._keepMarkdown = keepMarkdown
|
||||
def setKeepMarkdown(self, state: bool):
|
||||
"""Keep original markdown during build."""
|
||||
self._keepMarkdown = state
|
||||
return
|
||||
|
||||
##
|
||||
@@ -279,10 +305,9 @@ class Tokenizer(ABC):
|
||||
def doConvert(self):
|
||||
raise NotImplementedError
|
||||
|
||||
def addRootHeading(self, theHandle):
|
||||
"""Add a heading at the start of a new root folder.
|
||||
"""
|
||||
if not self._project.tree.checkType(theHandle, nwItemType.ROOT):
|
||||
def addRootHeading(self, tHandle: str) -> bool:
|
||||
"""Add a heading at the start of a new root folder."""
|
||||
if not self._project.tree.checkType(tHandle, nwItemType.ROOT):
|
||||
return False
|
||||
|
||||
if self._isFirst:
|
||||
@@ -291,7 +316,7 @@ class Tokenizer(ABC):
|
||||
else:
|
||||
textAlign = self.A_PBB | self.A_CENTRE
|
||||
|
||||
theItem = self._project.tree[theHandle]
|
||||
theItem = self._project.tree[tHandle]
|
||||
locNotes = self._localLookup("Notes")
|
||||
theTitle = f"{locNotes}: {theItem.itemName}"
|
||||
self._tokens = []
|
||||
@@ -299,22 +324,22 @@ class Tokenizer(ABC):
|
||||
self.T_TITLE, 0, theTitle, None, textAlign
|
||||
))
|
||||
if self._keepMarkdown:
|
||||
self._theMarkdown.append(f"# {theTitle}\n\n")
|
||||
self._allMarkdown.append(f"# {theTitle}\n\n")
|
||||
|
||||
return True
|
||||
|
||||
def setText(self, theHandle, theText=None):
|
||||
def setText(self, tHandle: str, text: str | None = None) -> bool:
|
||||
"""Set the text for the tokenizer from a handle. If theText is
|
||||
not set, load it from the file.
|
||||
"""
|
||||
self._nwItem = self._project.tree[theHandle]
|
||||
self._nwItem = self._project.tree[tHandle]
|
||||
if self._nwItem is None:
|
||||
return False
|
||||
|
||||
if theText is None:
|
||||
theText = self._project.storage.getDocument(theHandle).readDocument() or ""
|
||||
if text is None:
|
||||
text = self._project.storage.getDocument(tHandle).readDocument() or ""
|
||||
|
||||
self._text = theText
|
||||
self._text = text
|
||||
|
||||
docSize = len(self._text)
|
||||
if docSize > nwConst.MAX_DOCSIZE:
|
||||
@@ -331,8 +356,7 @@ class Tokenizer(ABC):
|
||||
return True
|
||||
|
||||
def doPreProcessing(self):
|
||||
"""Run trough the various replace doctionaries.
|
||||
"""
|
||||
"""Run trough the various replace dictionaries."""
|
||||
# Process the user's auto-replace dictionary
|
||||
autoReplace = self._project.data.autoReplace
|
||||
if len(autoReplace) > 0:
|
||||
@@ -582,7 +606,7 @@ class Tokenizer(ABC):
|
||||
tmpMarkdown.append("\n")
|
||||
|
||||
if self._keepMarkdown:
|
||||
self._theMarkdown.append("".join(tmpMarkdown))
|
||||
self._allMarkdown.append("".join(tmpMarkdown))
|
||||
|
||||
# Second Pass
|
||||
# ===========
|
||||
@@ -610,7 +634,7 @@ class Tokenizer(ABC):
|
||||
|
||||
return
|
||||
|
||||
def doHeaders(self):
|
||||
def doHeaders(self) -> bool:
|
||||
"""Apply formatting to the text headers for novel files. This
|
||||
also applies chapter and scene numbering.
|
||||
"""
|
||||
@@ -708,11 +732,10 @@ class Tokenizer(ABC):
|
||||
|
||||
return True
|
||||
|
||||
def saveRawMarkdown(self, savePath):
|
||||
"""Save the data to a plain text file.
|
||||
"""
|
||||
def saveRawMarkdown(self, savePath: str | Path):
|
||||
"""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._allMarkdown:
|
||||
outFile.write(nwdPage)
|
||||
return
|
||||
|
||||
|
||||
+29
-38
@@ -22,10 +22,14 @@ General Public License for more details.
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from novelwriter.constants import nwLabels
|
||||
from novelwriter.core.project import NWProject
|
||||
from novelwriter.core.tokenizer import Tokenizer
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -36,8 +40,8 @@ class ToMarkdown(Tokenizer):
|
||||
M_STD = 0 # Standard Markdown
|
||||
M_GH = 1 # GitHub Markdown
|
||||
|
||||
def __init__(self, theProject):
|
||||
super().__init__(theProject)
|
||||
def __init__(self, project: NWProject):
|
||||
super().__init__(project)
|
||||
|
||||
self._genMode = self.M_STD
|
||||
self._fullMD = []
|
||||
@@ -49,7 +53,8 @@ class ToMarkdown(Tokenizer):
|
||||
##
|
||||
|
||||
@property
|
||||
def fullMD(self):
|
||||
def fullMD(self) -> list:
|
||||
"""Return the markdown as a list."""
|
||||
return self._fullMD
|
||||
|
||||
##
|
||||
@@ -68,9 +73,8 @@ class ToMarkdown(Tokenizer):
|
||||
# Class Methods
|
||||
##
|
||||
|
||||
def getFullResultSize(self):
|
||||
"""Return the size of the full Markdown result.
|
||||
"""
|
||||
def getFullResultSize(self) -> int:
|
||||
"""Return the size of the full Markdown result."""
|
||||
return sum([len(x) for x in self._fullMD])
|
||||
|
||||
def doConvert(self):
|
||||
@@ -166,49 +170,36 @@ class ToMarkdown(Tokenizer):
|
||||
|
||||
return
|
||||
|
||||
def saveMarkdown(self, savePath):
|
||||
"""Save the data to a plain text file.
|
||||
"""
|
||||
with open(savePath, mode="w", encoding="utf-8") as outFile:
|
||||
theText = "".join(self._fullMD)
|
||||
outFile.write(theText)
|
||||
|
||||
def saveMarkdown(self, path: str | Path):
|
||||
"""Save the data to a plain text file."""
|
||||
with open(path, mode="w", encoding="utf-8") as outFile:
|
||||
outFile.write("".join(self._fullMD))
|
||||
return
|
||||
|
||||
def replaceTabs(self, nSpaces=8, spaceChar=" "):
|
||||
"""Replace tabs with spaces.
|
||||
"""
|
||||
fullMD = []
|
||||
eightSpace = spaceChar*nSpaces
|
||||
for aPage in self._fullMD:
|
||||
fullMD.append(aPage.replace("\t", eightSpace))
|
||||
|
||||
self._fullMD = fullMD
|
||||
def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " "):
|
||||
"""Replace tabs with spaces."""
|
||||
spaces = spaceChar*nSpaces
|
||||
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
|
||||
return
|
||||
|
||||
##
|
||||
# Internal Functions
|
||||
##
|
||||
|
||||
def _formatKeywords(self, tText, tStyle):
|
||||
"""Apply Markdown formatting to keywords.
|
||||
"""
|
||||
isValid, theBits, _ = self._project.index.scanThis("@"+tText)
|
||||
if not isValid or not theBits:
|
||||
def _formatKeywords(self, text: str, style: int) -> str:
|
||||
"""Apply Markdown formatting to keywords."""
|
||||
valid, bits, _ = self._project.index.scanThis("@"+text)
|
||||
if not valid or not bits:
|
||||
return ""
|
||||
|
||||
retText = ""
|
||||
if theBits[0] in nwLabels.KEY_NAME:
|
||||
retText += f"**{nwLabels.KEY_NAME[theBits[0]]}:** "
|
||||
result = ""
|
||||
if bits[0] in nwLabels.KEY_NAME:
|
||||
result += f"**{nwLabels.KEY_NAME[bits[0]]}:** "
|
||||
if len(bits) > 1:
|
||||
result += ", ".join(bits[1:])
|
||||
|
||||
if len(theBits) > 1:
|
||||
retText += ", ".join(theBits[1:])
|
||||
result += " \n" if style & self.A_Z_BTMMRG else "\n\n"
|
||||
|
||||
if tStyle & self.A_Z_BTMMRG:
|
||||
retText += " \n"
|
||||
else:
|
||||
retText += "\n\n"
|
||||
|
||||
return retText
|
||||
return result
|
||||
|
||||
# END Class ToMarkdown
|
||||
|
||||
Reference in New Issue
Block a user