From aea41922132fe5a4eafafc3e6c21ee09fc11cd27 Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sat, 2 Mar 2024 15:24:07 +0100 Subject: [PATCH] Add option to ignore certain keywords in manuscript (#1132) --- novelwriter/core/buildsettings.py | 2 ++ novelwriter/core/docbuild.py | 3 ++- novelwriter/core/tokenizer.py | 38 +++++++++++++++----------- novelwriter/tools/manussettings.py | 43 ++++++++++++++++++++++++++---- 4 files changed, 65 insertions(+), 21 deletions(-) diff --git a/novelwriter/core/buildsettings.py b/novelwriter/core/buildsettings.py index 44f186cb..8557a523 100644 --- a/novelwriter/core/buildsettings.py +++ b/novelwriter/core/buildsettings.py @@ -69,6 +69,7 @@ SETTINGS_TEMPLATE = { "text.includeComments": (bool, False), "text.includeKeywords": (bool, False), "text.includeBodyText": (bool, True), + "text.ignoredKeywords": (str, ""), "text.addNoteHeadings": (bool, True), "format.textFont": (str, CONFIG.textFont), "format.textSize": (int, 12), @@ -112,6 +113,7 @@ SETTINGS_LABELS = { "text.includeComments": QT_TRANSLATE_NOOP("Builds", "Include Comments"), "text.includeKeywords": QT_TRANSLATE_NOOP("Builds", "Include Keywords"), "text.includeBodyText": QT_TRANSLATE_NOOP("Builds", "Include Body Text"), + "text.ignoredKeywords": QT_TRANSLATE_NOOP("Builds", "Ignore These Keywords"), "text.grpInsert": QT_TRANSLATE_NOOP("Builds", "Insert Content"), "text.addNoteHeadings": QT_TRANSLATE_NOOP("Builds", "Add Titles for Notes"), diff --git a/novelwriter/core/docbuild.py b/novelwriter/core/docbuild.py index a7966074..e43816f8 100644 --- a/novelwriter/core/docbuild.py +++ b/novelwriter/core/docbuild.py @@ -303,10 +303,11 @@ class NWBuildDocument: bldObj.setJustify(self._build.getBool("format.justifyText")) bldObj.setLineHeight(self._build.getFloat("format.lineHeight")) + bldObj.setBodyText(self._build.getBool("text.includeBodyText")) bldObj.setSynopsis(self._build.getBool("text.includeSynopsis")) bldObj.setComments(self._build.getBool("text.includeComments")) bldObj.setKeywords(self._build.getBool("text.includeKeywords")) - bldObj.setBodyText(self._build.getBool("text.includeBodyText")) + bldObj.setIgnoredKeywords(self._build.getStr("text.ignoredKeywords")) if isinstance(bldObj, ToHtml): bldObj.setStyles(self._build.getBool("html.addStyles")) diff --git a/novelwriter/core/tokenizer.py b/novelwriter/core/tokenizer.py index ef3136fd..67871dfb 100644 --- a/novelwriter/core/tokenizer.py +++ b/novelwriter/core/tokenizer.py @@ -130,16 +130,17 @@ class Tokenizer(ABC): self._tokens: list[tuple[int, int, str, list[tuple[int, int]], int]] = [] # User Settings - self._textFont = "Serif" # Output text font - self._textSize = 11 # Output text size - self._textFixed = False # Fixed width text - self._lineHeight = 1.15 # Line height in units of em - self._blockIndent = 4.00 # Block indent in units of em - self._doJustify = False # Justify text - self._doBodyText = True # Include body text - self._doSynopsis = False # Also process synopsis comments - self._doComments = False # Also process comments - self._doKeywords = False # Also process keywords like tags and references + self._textFont = "Serif" # Output text font + self._textSize = 11 # Output text size + self._textFixed = False # Fixed width text + self._lineHeight = 1.15 # Line height in units of em + self._blockIndent = 4.00 # Block indent in units of em + self._doJustify = False # Justify text + self._doBodyText = True # Include body text + self._doSynopsis = False # Also process synopsis comments + self._doComments = False # Also process comments + self._doKeywords = False # Also process keywords like tags and references + self._skipKeywords = set() # Keywords to ignore # Margins self._marginTitle = (1.000, 0.500) @@ -363,6 +364,11 @@ class Tokenizer(ABC): self._doKeywords = state return + def setIgnoredKeywords(self, keywords: str) -> None: + """Comma separated string of keywords to ignore.""" + self._skipKeywords = set(x.lower().strip() for x in keywords.split(",")) + return + def setKeepMarkdown(self, state: bool) -> None: """Keep original markdown during build.""" self._keepMarkdown = state @@ -530,11 +536,13 @@ class Tokenizer(ABC): tmpMarkdown.append("%s\n" % aLine) elif aLine[0] == "@": - self._tokens.append(( - self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign - )) - if self._doKeywords and self._keepMarkdown: - tmpMarkdown.append("%s\n" % aLine) + valid, bits, _ = self._project.index.scanThis(aLine) + if valid and bits and bits[0] not in self._skipKeywords: + self._tokens.append(( + self.T_KEYWORD, nHead, aLine[1:].strip(), [], sAlign + )) + if self._doKeywords and self._keepMarkdown: + tmpMarkdown.append("%s\n" % aLine) elif aLine[:2] == "# ": nHead += 1 diff --git a/novelwriter/tools/manussettings.py b/novelwriter/tools/manussettings.py index d27b8acf..14a3d529 100644 --- a/novelwriter/tools/manussettings.py +++ b/novelwriter/tools/manussettings.py @@ -37,7 +37,7 @@ from PyQt5.QtWidgets import ( ) from novelwriter import CONFIG, SHARED -from novelwriter.constants import nwHeadFmt, nwLabels, trConst +from novelwriter.constants import nwHeadFmt, nwKeyWords, nwLabels, trConst from novelwriter.core.buildsettings import BuildSettings, FilterMode from novelwriter.extensions.switch import NSwitch from novelwriter.extensions.modified import NComboBox, NDoubleSpinBox, NSpinBox @@ -966,16 +966,32 @@ class _ContentTab(NScrollableForm): iPx = SHARED.theme.baseIconSize # Text Content + self.incBodyText = NSwitch(self, height=iPx) self.incSynopsis = NSwitch(self, height=iPx) self.incComments = NSwitch(self, height=iPx) self.incKeywords = NSwitch(self, height=iPx) - self.incBodyText = NSwitch(self, height=iPx) + + self.ignoredKeywords = QLineEdit(self) + + self.mnKeywords = QMenu(self) + for keyword in nwKeyWords.VALID_KEYS: + self.mnKeywords.addAction( + trConst(nwLabels.KEY_NAME[keyword]), + lambda keyword=keyword: self._updateIgnoredKeywords(keyword) + ) + + self.ignoredKeywordsButton = QToolButton(self) + self.ignoredKeywordsButton.setIcon(SHARED.theme.getIcon("add")) + self.ignoredKeywordsButton.setMenu(self.mnKeywords) + self.ignoredKeywordsButton.setPopupMode(QToolButton.ToolButtonPopupMode.InstantPopup) self.addGroupLabel(self._build.getLabel("text.grpContent")) + self.addRow(self._build.getLabel("text.includeBodyText"), self.incBodyText) self.addRow(self._build.getLabel("text.includeSynopsis"), self.incSynopsis) self.addRow(self._build.getLabel("text.includeComments"), self.incComments) self.addRow(self._build.getLabel("text.includeKeywords"), self.incKeywords) - self.addRow(self._build.getLabel("text.includeBodyText"), self.incBodyText) + self.addRow(self._build.getLabel("text.ignoredKeywords"), self.ignoredKeywords, + button=self.ignoredKeywordsButton, stretch=(1, 1)) # Insert Content self.addNoteHead = NSwitch(self, height=iPx) @@ -990,22 +1006,39 @@ class _ContentTab(NScrollableForm): def loadContent(self) -> None: """Populate the widgets.""" + self.incBodyText.setChecked(self._build.getBool("text.includeBodyText")) self.incSynopsis.setChecked(self._build.getBool("text.includeSynopsis")) self.incComments.setChecked(self._build.getBool("text.includeComments")) self.incKeywords.setChecked(self._build.getBool("text.includeKeywords")) - self.incBodyText.setChecked(self._build.getBool("text.includeBodyText")) + self.ignoredKeywords.setText(self._build.getStr("text.ignoredKeywords")) self.addNoteHead.setChecked(self._build.getBool("text.addNoteHeadings")) + self._updateIgnoredKeywords() return def saveContent(self) -> None: """Save choices back into build object.""" + self._updateIgnoredKeywords() + self._build.setValue("text.includeBodyText", self.incBodyText.isChecked()) self._build.setValue("text.includeSynopsis", self.incSynopsis.isChecked()) self._build.setValue("text.includeComments", self.incComments.isChecked()) self._build.setValue("text.includeKeywords", self.incKeywords.isChecked()) - self._build.setValue("text.includeBodyText", self.incBodyText.isChecked()) + self._build.setValue("text.ignoredKeywords", self.ignoredKeywords.text()) self._build.setValue("text.addNoteHeadings", self.addNoteHead.isChecked()) return + ## + # Internal Functions + ## + + def _updateIgnoredKeywords(self, keyword: str | None = None) -> None: + """Update the ignored keywords list.""" + current = [x.lower().strip() for x in self.ignoredKeywords.text().split(",")] + if keyword: + current.append(keyword) + verified = set(x for x in current if x in nwKeyWords.VALID_KEYS) + self.ignoredKeywords.setText(", ".join(verified)) + return + # END Class _ContentTab