diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py index c46bff04..13f75407 100644 --- a/novelwriter/core/index.py +++ b/novelwriter/core/index.py @@ -1494,4 +1494,4 @@ def processComment(text: str) -> tuple[nwComment, str, str, int, int]: if content and (clean := classifier.strip().lower()) in CLASSIFIERS: term = "ERR" if term and clean not in TERMS else term.strip() return CLASSIFIERS[clean], term, content.strip(), text.find(".") + 1, text.find(":") + 1 - return nwComment.PLAIN, "", check, 0, 0 + return nwComment.IGNORE if text.startswith("%~") else nwComment.PLAIN, "", check, 0, 0 diff --git a/novelwriter/core/tohtml.py b/novelwriter/core/tohtml.py index 604799be..1384c96e 100644 --- a/novelwriter/core/tohtml.py +++ b/novelwriter/core/tohtml.py @@ -94,7 +94,7 @@ class ToHtml(Tokenizer): # Internals self._trMap = {} - self._usedNotes = set() + self._usedNotes: dict[str, int] = {} self.setReplaceUnicode(False) return @@ -313,9 +313,9 @@ class ToHtml(Tokenizer): lines = [] lines.append(f"

{footnotes}

\n") lines.append("
    \n") - for key, (index, content) in self._footnotes.items(): - if key in self._usedNotes: - text = "

    ".join(self._formatText(t, f, tags) for t, f in content) + for key, index in self._usedNotes.items(): + if content := self._footnotes.get(key): + text = self._formatText(*content, tags) lines.append(f"

  1. {text}

  2. \n") lines.append("
\n") @@ -482,9 +482,12 @@ class ToHtml(Tokenizer): for pos, fmt, data in reversed(tFmt): html = "" if fmt == self.FMT_FNOTE: - self._usedNotes.add(data) - index = self._footnotes.get(data, (0, ""))[0] or "ERR" - html = f"[{index}]" + if data in self._footnotes: + index = len(self._usedNotes) + 1 + self._usedNotes[data] = index + html = f"{index}" + else: + html = "ERR" else: html = tags.get(fmt, "ERR") temp = f"{temp[:pos]}{html}{temp[pos:]}" diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index db1a5f3f..ad9ea768 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -49,7 +49,7 @@ ESCAPES = {r"\*": "*", r"\~": "~", r"\_": "_", r"\[": "[", r"\]": "]", r"\ ": "" RX_ESC = re.compile("|".join([re.escape(k) for k in ESCAPES.keys()]), flags=re.DOTALL) T_Formats = list[tuple[int, int, str]] -T_Comment = tuple[int, list[tuple[str, T_Formats]]] +T_Comment = tuple[str, T_Formats] def stripEscape(text: str) -> str: @@ -561,9 +561,7 @@ class Tokenizer(ABC): tmpMarkdown.append(f"{aLine}\n") elif cStyle == nwComment.FOOTNOTE: tLine, tFmt = self._extractFormats(cText, skip=self.FMT_FNOTE) - if cKey not in self._footnotes: - self._footnotes[cKey] = (len(self._footnotes) + 1, []) - self._footnotes[cKey][1].append((tLine, tFmt)) + self._footnotes[cKey] = (tLine, tFmt) else: tLine, tFmt = self._extractFormats(cText) self._tokens.append(( diff --git a/novelwriter/core/tomd.py b/novelwriter/core/tomd.py index 1d5fa318..bcf8a639 100644 --- a/novelwriter/core/tomd.py +++ b/novelwriter/core/tomd.py @@ -89,7 +89,7 @@ class ToMarkdown(Tokenizer): self._genMode = self.M_STD self._fullMD: list[str] = [] self._preserveBreaks = True - self._usedNotes = set() + self._usedNotes: dict[str, int] = {} return ## @@ -208,11 +208,10 @@ class ToMarkdown(Tokenizer): lines = [] lines.append(f"### {footnotes}\n\n") - for key, (index, content) in self._footnotes.items(): - if key in self._usedNotes: + for key, index in self._usedNotes.items(): + if content := self._footnotes.get(key): marker = f"{index}. " - indent = "\n\n"+" "*len(marker) - text = indent.join(self._formatText(t, f, tags) for t, f in content) + text = self._formatText(*content, tags) lines.append(f"{marker}{text}\n") lines.append("\n") @@ -247,9 +246,12 @@ class ToMarkdown(Tokenizer): for pos, fmt, data in reversed(tFmt): md = "" if fmt == self.FMT_FNOTE: - self._usedNotes.add(data) - index = self._footnotes.get(data, (0, ""))[0] or "ERR" - md = f"[{index}]" + if data in self._footnotes: + index = len(self._usedNotes) + 1 + self._usedNotes[data] = index + md = f"[{index}]" + else: + md = "[ERR]" else: md = tags.get(fmt, "") temp = f"{temp[:pos]}{md}{temp[pos:]}" diff --git a/novelwriter/core/toodt.py b/novelwriter/core/toodt.py index e82835e9..67867133 100644 --- a/novelwriter/core/toodt.py +++ b/novelwriter/core/toodt.py @@ -30,7 +30,6 @@ import logging import xml.etree.ElementTree as ET from collections.abc import Sequence -from copy import deepcopy from datetime import datetime from hashlib import sha256 from pathlib import Path @@ -412,7 +411,6 @@ class ToOdt(Tokenizer): def doConvert(self) -> None: """Convert the list of text tokens into XML elements.""" self._result = "" # Not used, but cleared just in case - self._preProcessNotes() pFmt: list[T_Formats] = [] pText = [] @@ -687,7 +685,9 @@ class ToOdt(Tokenizer): elif fFmt == self.FMT_SUB_E: xFmt &= M_SUB elif fFmt == self.FMT_FNOTE: - parProc.appendNode(self._etNotes.get(fData)) + parProc.appendNode(self._generateFootnote(fData)) + elif fFmt == self.FMT_STRIP: + pass else: pErr += 1 @@ -758,16 +758,11 @@ class ToOdt(Tokenizer): return style.name - def _preProcessNotes(self) -> None: - """Generate XML elements for footnotes.""" - fStyle = ODTParagraphStyle("New") - sStyle = ODTParagraphStyle("New") - sStyle.setTextIndent("0.000cm") - sStyle.setMarginLeft(self._mLeftFoot) - update = [key for key in self._footnotes.keys() if key not in self._etNotes] - for key in update: - cStyle = fStyle + def _generateFootnote(self, key: str) -> ET.Element | None: + """Generate a footnote XML object.""" + if content := self._footnotes.get(key): self._nNote += 1 + nStyle = ODTParagraphStyle("New") xNote = ET.Element(_mkTag("text", "note"), attrib={ _mkTag("text", "id"): f"ftn{self._nNote}", _mkTag("text", "note-class"): "footnote", @@ -775,11 +770,9 @@ class ToOdt(Tokenizer): xCite = ET.SubElement(xNote, _mkTag("text", "note-citation")) xCite.text = str(self._nNote) xBody = ET.SubElement(xNote, _mkTag("text", "note-body")) - for text, fmt in self._footnotes[key][1]: - self._addTextPar(xBody, "Footnote", cStyle, text, tFmt=fmt) - cStyle = sStyle - self._etNotes[key] = xNote - return + self._addTextPar(xBody, "Footnote", nStyle, content[0], tFmt=content[1]) + return xNote + return None def _emToCm(self, value: float) -> str: """Converts an em value to centimetres.""" @@ -1584,16 +1577,14 @@ class XMLParagraph: def appendNode(self, xNode: ET.Element | None) -> None: """Append an XML node to the paragraph.""" if xNode: - # We must make a copy in case the node is reused - xCopy = deepcopy(xNode) if self._nState in (X_ROOT_TEXT, X_ROOT_TAIL): - self._xRoot.append(xCopy) - self._xTail = xCopy + self._xRoot.append(xNode) + self._xTail = xNode self._xTail.tail = "" self._nState = X_ROOT_TAIL elif self._nState in (X_SPAN_TEXT, X_SPAN_SING): - self._xTail.append(xCopy) - self._xSing = xCopy + self._xTail.append(xNode) + self._xSing = xNode self._xSing.tail = "" self._nState = X_SPAN_SING return diff --git a/novelwriter/enum.py b/novelwriter/enum.py index 1781eb2f..eae462a0 100644 --- a/novelwriter/enum.py +++ b/novelwriter/enum.py @@ -65,12 +65,13 @@ class nwItemLayout(Enum): class nwComment(Enum): PLAIN = 0 - SYNOPSIS = 1 - SHORT = 2 - NOTE = 3 - FOOTNOTE = 4 - COMMENT = 5 - STORY = 6 + IGNORE = 1 + SYNOPSIS = 2 + SHORT = 3 + NOTE = 4 + FOOTNOTE = 5 + COMMENT = 6 + STORY = 7 # END Enum nwComment