Make the footnote registry per-file

This commit is contained in:
Veronica Berglyd Olsen
2024-04-26 20:12:57 +02:00
parent 52e8ae9cb6
commit b585b8ecd1
5 changed files with 42 additions and 38 deletions
+3 -3
View File
@@ -26,12 +26,12 @@ from __future__ import annotations
import json import json
import logging import logging
from time import time
from pathlib import Path from pathlib import Path
from time import time
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.common import formatTimeStamp from novelwriter.common import formatTimeStamp
from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, nwHtmlUnicode from novelwriter.constants import nwHeadFmt, nwHtmlUnicode, nwKeyWords, nwLabels
from novelwriter.core.project import NWProject from novelwriter.core.project import NWProject
from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape from novelwriter.core.tokenizer import T_Formats, Tokenizer, stripEscape
@@ -306,7 +306,7 @@ class ToHtml(Tokenizer):
def appendFootnotes(self) -> None: def appendFootnotes(self) -> None:
"""Append the footnotes in the buffer.""" """Append the footnotes in the buffer."""
if self._footnotes: if self._usedNotes:
tags = HTML4_TAGS if self._genMode == self.M_PREVIEW else HTML5_TAGS tags = HTML4_TAGS if self._genMode == self.M_PREVIEW else HTML5_TAGS
footnotes = self._localLookup("Footnotes") footnotes = self._localLookup("Footnotes")
+34 -31
View File
@@ -24,18 +24,18 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
""" """
from __future__ import annotations from __future__ import annotations
import re
import json import json
import logging import logging
import re
from abc import ABC, abstractmethod from abc import ABC, abstractmethod
from time import time
from pathlib import Path
from functools import partial from functools import partial
from pathlib import Path
from time import time
from PyQt5.QtCore import QCoreApplication, QRegularExpression from PyQt5.QtCore import QCoreApplication, QRegularExpression
from novelwriter.common import formatTimeStamp, numberToRoman, checkInt from novelwriter.common import checkInt, formatTimeStamp, numberToRoman
from novelwriter.constants import ( from novelwriter.constants import (
nwHeadFmt, nwKeyWords, nwLabels, nwRegEx, nwShortcode, nwUnicode, trConst nwHeadFmt, nwKeyWords, nwLabels, nwRegEx, nwShortcode, nwUnicode, trConst
) )
@@ -122,18 +122,19 @@ class Tokenizer(ABC):
self._project = project self._project = project
# Data Variables # Data Variables
self._text = "" # The raw text to be tokenized self._text = "" # The raw text to be tokenized
self._handle = None # The item handle currently being processed self._handle = None # The item handle currently being processed
self._result = "" # The result of the last document self._result = "" # The result of the last document
self._keepMD = False # Whether to keep the markdown text
self._keepMarkdown = False # Whether to keep the markdown text # Tokens and Meta Data (Per Document)
self._allMarkdown = [] # The result novelWriter markdown of all documents
# Processed Tokens and Meta Data
self._tokens: list[tuple[int, int, str, T_Formats, int]] = [] self._tokens: list[tuple[int, int, str, T_Formats, int]] = []
self._footnotes: dict[str, T_Comment] = {} self._footnotes: dict[str, T_Comment] = {}
# Tokens and Meta Data (Per Instance)
self._counts: dict[str, int] = {} self._counts: dict[str, int] = {}
self._outline: dict[str, str] = {} self._outline: dict[str, str] = {}
self._markdown: list[str] = []
# User Settings # User Settings
self._textFont = "Serif" # Output text font self._textFont = "Serif" # Output text font
@@ -231,7 +232,7 @@ class Tokenizer(ABC):
@property @property
def allMarkdown(self) -> list[str]: def allMarkdown(self) -> list[str]:
"""The combined novelWriter Markdown text.""" """The combined novelWriter Markdown text."""
return self._allMarkdown return self._markdown
@property @property
def textStats(self) -> dict[str, int]: def textStats(self) -> dict[str, int]:
@@ -398,7 +399,7 @@ class Tokenizer(ABC):
def setKeepMarkdown(self, state: bool) -> None: def setKeepMarkdown(self, state: bool) -> None:
"""Keep original markdown during build.""" """Keep original markdown during build."""
self._keepMarkdown = state self._keepMD = state
return return
## ##
@@ -428,8 +429,8 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_TITLE, 1, title, [], textAlign self.T_TITLE, 1, title, [], textAlign
)) ))
if self._keepMarkdown: if self._keepMD:
self._allMarkdown.append(f"#! {title}\n\n") self._markdown.append(f"#! {title}\n\n")
return return
@@ -484,6 +485,7 @@ class Tokenizer(ABC):
nHead = 0 nHead = 0
breakNext = False breakNext = False
tmpMarkdown = [] tmpMarkdown = []
tHandle = self._handle or ""
for aLine in self._text.splitlines(): for aLine in self._text.splitlines():
sLine = aLine.strip().lower() sLine = aLine.strip().lower()
@@ -492,7 +494,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE self.T_EMPTY, nHead, "", [], self.A_NONE
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
continue continue
@@ -550,24 +552,24 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_SYNOPSIS, nHead, tLine, tFmt, sAlign self.T_SYNOPSIS, nHead, tLine, tFmt, sAlign
)) ))
if self._doSynopsis and self._keepMarkdown: if self._doSynopsis and self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.SHORT: elif cStyle == nwComment.SHORT:
tLine, tFmt = self._extractFormats(cText) tLine, tFmt = self._extractFormats(cText)
self._tokens.append(( self._tokens.append((
self.T_SHORT, nHead, tLine, tFmt, sAlign self.T_SHORT, nHead, tLine, tFmt, sAlign
)) ))
if self._doSynopsis and self._keepMarkdown: if self._doSynopsis and self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif cStyle == nwComment.FOOTNOTE: elif cStyle == nwComment.FOOTNOTE:
tLine, tFmt = self._extractFormats(cText, skip=self.FMT_FNOTE) tLine, tFmt = self._extractFormats(cText, skip=self.FMT_FNOTE)
self._footnotes[cKey] = (tLine, tFmt) self._footnotes[f"{tHandle}:{cKey}"] = (tLine, tFmt)
else: else:
tLine, tFmt = self._extractFormats(cText) tLine, tFmt = self._extractFormats(cText)
self._tokens.append(( self._tokens.append((
self.T_COMMENT, nHead, tLine, tFmt, sAlign self.T_COMMENT, nHead, tLine, tFmt, sAlign
)) ))
if self._doComments and self._keepMarkdown: if self._doComments and self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("@"): elif aLine.startswith("@"):
@@ -581,7 +583,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign
)) ))
if self._doKeywords and self._keepMarkdown: if self._doKeywords and self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("# ", "#! ")): elif aLine.startswith(("# ", "#! ")):
@@ -617,7 +619,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
tType, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("## ", "##! ")): elif aLine.startswith(("## ", "##! ")):
@@ -652,7 +654,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
tType, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith(("### ", "###! ")): elif aLine.startswith(("### ", "###! ")):
@@ -693,7 +695,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
tType, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
elif aLine.startswith("#### "): elif aLine.startswith("#### "):
@@ -723,7 +725,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
tType, nHead, tText, [], tStyle tType, nHead, tText, [], tStyle
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
else: else:
@@ -771,7 +773,7 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_TEXT, nHead, tLine, tFmt, sAlign self.T_TEXT, nHead, tLine, tFmt, sAlign
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append(f"{aLine}\n") tmpMarkdown.append(f"{aLine}\n")
# If we have content, turn off the first page flag # If we have content, turn off the first page flag
@@ -790,9 +792,9 @@ class Tokenizer(ABC):
self._tokens.append(( self._tokens.append((
self.T_EMPTY, nHead, "", [], self.A_NONE self.T_EMPTY, nHead, "", [], self.A_NONE
)) ))
if self._keepMarkdown: if self._keepMD:
tmpMarkdown.append("\n") tmpMarkdown.append("\n")
self._allMarkdown.append("".join(tmpMarkdown)) self._markdown.append("".join(tmpMarkdown))
# Second Pass # Second Pass
# =========== # ===========
@@ -954,7 +956,7 @@ class Tokenizer(ABC):
def saveRawMarkdown(self, path: str | Path) -> None: def saveRawMarkdown(self, path: str | Path) -> None:
"""Save the raw text to a plain text file.""" """Save the raw text to a plain text file."""
with open(path, mode="w", encoding="utf-8") as outFile: with open(path, mode="w", encoding="utf-8") as outFile:
for nwdPage in self._allMarkdown: for nwdPage in self._markdown:
outFile.write(nwdPage) outFile.write(nwdPage)
return return
@@ -969,7 +971,7 @@ class Tokenizer(ABC):
"buildTimeStr": formatTimeStamp(timeStamp), "buildTimeStr": formatTimeStamp(timeStamp),
}, },
"text": { "text": {
"nwd": [page.rstrip("\n").split("\n") for page in self._allMarkdown], "nwd": [page.rstrip("\n").split("\n") for page in self._markdown],
} }
} }
with open(path, mode="w", encoding="utf-8") as fObj: with open(path, mode="w", encoding="utf-8") as fObj:
@@ -1007,6 +1009,7 @@ class Tokenizer(ABC):
# Match Shortcode w/Values # Match Shortcode w/Values
rxItt = self._rxShortCodeVals.globalMatch(text, 0) rxItt = self._rxShortCodeVals.globalMatch(text, 0)
tHandle = self._handle or ""
while rxItt.hasNext(): while rxItt.hasNext():
rxMatch = rxItt.next() rxMatch = rxItt.next()
kind = self._shortCodeVals.get(rxMatch.captured(1).lower(), 0) kind = self._shortCodeVals.get(rxMatch.captured(1).lower(), 0)
@@ -1014,7 +1017,7 @@ class Tokenizer(ABC):
rxMatch.capturedStart(0), rxMatch.capturedStart(0),
rxMatch.capturedLength(0), rxMatch.capturedLength(0),
self.FMT_STRIP if kind == skip else kind, self.FMT_STRIP if kind == skip else kind,
rxMatch.captured(2), f"{tHandle}:{rxMatch.captured(2)}",
)) ))
# Post-process text and format # Post-process text and format
+3 -3
View File
@@ -202,7 +202,7 @@ class ToMarkdown(Tokenizer):
def appendFootnotes(self) -> None: def appendFootnotes(self) -> None:
"""Append the footnotes in the buffer.""" """Append the footnotes in the buffer."""
if self._footnotes: if self._usedNotes:
tags = STD_MD if self._genMode == self.M_STD else EXT_MD tags = STD_MD if self._genMode == self.M_STD else EXT_MD
footnotes = self._localLookup("Footnotes") footnotes = self._localLookup("Footnotes")
@@ -232,8 +232,8 @@ class ToMarkdown(Tokenizer):
"""Replace tabs with spaces.""" """Replace tabs with spaces."""
spaces = spaceChar*nSpaces spaces = spaceChar*nSpaces
self._fullMD = [p.replace("\t", spaces) for p in self._fullMD] self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
if self._keepMarkdown: if self._keepMD:
self._allMarkdown = [p.replace("\t", spaces) for p in self._allMarkdown] self._markdown = [p.replace("\t", spaces) for p in self._markdown]
return return
## ##
+1 -1
View File
@@ -47,7 +47,7 @@ version = {attr = "novelwriter.__version__"}
include = ["novelwriter*"] include = ["novelwriter*"]
[tool.isort] [tool.isort]
py_version="38" py_version="310"
line_length = 99 line_length = 99
wrap_length = 79 wrap_length = 79
multi_line_output = 5 multi_line_output = 5
+1
View File
@@ -2,3 +2,4 @@ flake8
flake8-pep585 flake8-pep585
flake8-pyproject flake8-pyproject
flake8-annotations flake8-annotations
isort