diff --git a/novelwriter/constants.py b/novelwriter/constants.py index a45033ad2..9567c3bb4 100644 --- a/novelwriter/constants.py +++ b/novelwriter/constants.py @@ -23,6 +23,8 @@ """ from __future__ import annotations +import re + from PyQt5.QtCore import QCoreApplication, QT_TRANSLATE_NOOP from novelwriter.enum import nwBuildFmt, nwItemClass, nwItemLayout, nwOutline @@ -59,9 +61,12 @@ class nwRegEx: FMT_EI = r"(? tuple[int, int, int]: if nwUnicode.U_EMDASH in text: text = text.replace(nwUnicode.U_EMDASH, " ") + # Strip shortcodes + if "[" in text: + text = nwRegEx.RX_SC.sub("", text) + for line in text.splitlines(): countPara = True @@ -1241,9 +1245,10 @@ def countWords(text: str) -> tuple[int, int, int]: continue if line[0] == "[": - if line.startswith(("[NEWPAGE]", "[NEW PAGE]", "[VSPACE]")): + check = line.lower() + if check.startswith(("[newpage]", "[new page]", "[vspace]")): continue - elif line.startswith("[VSPACE:") and line.endswith("]"): + elif check.startswith("[vspace:") and line.endswith("]"): continue elif line[0] == "#": diff --git a/novelwriter/gui/doceditor.py b/novelwriter/gui/doceditor.py index 1555e61eb..93053f200 100644 --- a/novelwriter/gui/doceditor.py +++ b/novelwriter/gui/doceditor.py @@ -55,6 +55,7 @@ from novelwriter.common import minmax, transferCase from novelwriter.constants import nwKeyWords, nwUnicode from novelwriter.core.index import countWords +from novelwriter.core.document import NWDocument from novelwriter.gui.dochighlight import GuiDocHighlighter from novelwriter.gui.editordocument import GuiTextDocument from novelwriter.extensions.wheeleventfilter import WheelEventFilter @@ -408,12 +409,6 @@ def updateTagHighLighting(self) -> None: self._qDocument.syntaxHighlighter.rehighlightByType(GuiDocHighlighter.BLOCK_META) return - def redrawText(self) -> None: - """Redraw the text by marking the document content as dirty.""" - self._qDocument.markContentsDirty(0, self._qDocument.characterCount()) - self.updateDocMargins() - return - def replaceText(self, text: str) -> None: """Replace the text of the current document with the provided text. This also clears undo history. @@ -735,19 +730,17 @@ def revealLocation(self) -> None: """Tell the user where on the file system the file in the editor is saved. """ - if self._nwDocument is None: - logger.error("No document open") - return - SHARED.info( - "
".join([ - self.tr("Document Details"), - "–"*40, - self.tr("Created: {0}").format(self._nwDocument.createdDate), - self.tr("Updated: {0}").format(self._nwDocument.updatedDate), - ]), - details=self.tr("File Location: {0}").format(self._nwDocument.fileLocation), - log=False - ) + if isinstance(self._nwDocument, NWDocument): + SHARED.info( + "
".join([ + self.tr("Document Details"), + "–"*40, + self.tr("Created: {0}").format(self._nwDocument.createdDate), + self.tr("Updated: {0}").format(self._nwDocument.updatedDate), + ]), + details=self.tr("File Location: {0}").format(self._nwDocument.fileLocation), + log=False + ) return def insertText(self, insert: str | nwDocInsert) -> bool: @@ -775,15 +768,15 @@ def insertText(self, insert: str | nwDocInsert) -> bool: newBlock = True goAfter = True elif insert == nwDocInsert.NEW_PAGE: - text = "[NEW PAGE]" + text = "[newpage]" newBlock = True goAfter = False elif insert == nwDocInsert.VSPACE_S: - text = "[VSPACE]" + text = "[vspace]" newBlock = True goAfter = False elif insert == nwDocInsert.VSPACE_M: - text = "[VSPACE:2]" + text = "[vspace:2]" newBlock = True goAfter = False else: diff --git a/sample/content/974e400180a99.nwd b/sample/content/974e400180a99.nwd index 0175798bc..5058f0ca7 100644 --- a/sample/content/974e400180a99.nwd +++ b/sample/content/974e400180a99.nwd @@ -1,11 +1,11 @@ %%~name: Page %%~path: 7031beac91f75/974e400180a99 %%~kind: NOVEL/DOCUMENT -%%~hash: 7547809e972205eb2a55dd988595e0aa1d01e139 -%%~date: Unknown/2023-08-25 16:51:54 -[NEW PAGE] -[VSPACE:2] +%%~hash: a0b0719e19763bf505bcf797bea0968c505c9b4a +%%~date: Unknown/2023-11-06 11:19:33 +[newpage] +[vspace:2] This is a plain page with some text on it. -If you want the text to start on a fresh page, add the [NEW PAGE] code above the text. You can also add empty paragraphs with the [VSPACE] code. The above code adds two empty paragraphs before the text starts. +If you want the text to start on a fresh page, add the [newpage] code above the text. You can also add empty paragraphs with the [vspace] code. The above code adds two empty paragraphs before the text starts. diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx index 34c50b8c3..67b86c1f9 100644 --- a/sample/nwProject.nwx +++ b/sample/nwProject.nwx @@ -1,6 +1,6 @@ - - + + Sample Project Sample Project Jane Smith @@ -36,7 +36,7 @@ Main - + Novel @@ -46,7 +46,7 @@ Title Page - + Page @@ -58,7 +58,7 @@ Chapter One - + Making a Scene diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py index d8b7917eb..03b497dad 100644 --- a/tests/test_core/test_core_index.py +++ b/tests/test_core/test_core_index.py @@ -1281,7 +1281,7 @@ def testCoreIndex_CountWords(): assert wC == 9 assert pC == 4 - # Formatting Codes + # Formatting Codes, Upper Case (Old Implementation) cC, wC, pC = countWords(( "Some text\n\n" "[NEWPAGE]\n\n" @@ -1297,4 +1297,29 @@ def testCoreIndex_CountWords(): assert wC == 13 assert pC == 5 + # Formatting Codes, Lower Case (Current Implementation) + cC, wC, pC = countWords(( + "Some text\n\n" + "[newpage]\n\n" + "more text\n\n" + "[new page]]\n\n" + "even more text\n\n" + "[vspace]\n\n" + "and some final text\n\n" + "[vspace:4]\n\n" + "THE END\n\n" + )) + assert cC == 58 + assert wC == 13 + assert pC == 5 + + # Check ShortCodes + cC, wC, pC = countWords(( + "Text with [b]bold[/b] text and padded [b] bold [/b] text.\n\n" + "Text with [b][i] nested [/i] emphasis [/b] in it.\n\n" + )) + assert cC == 78 + assert wC == 14 + assert pC == 2 + # END Test testCoreIndex_CountWords diff --git a/tests/test_core/test_core_tokenizer.py b/tests/test_core/test_core_tokenizer.py index df5590b39..d5cbee872 100644 --- a/tests/test_core/test_core_tokenizer.py +++ b/tests/test_core/test_core_tokenizer.py @@ -789,7 +789,7 @@ def testCoreToken_SpecialFormat(mockGUI): tokens._isFirst = True tokens._text = ( "# Title One\n\n" - "[NEWPAGE]\n\n" + "[newpage]\n\n" "# Title Two\n\n" ) tokens.tokenizeText() @@ -799,7 +799,7 @@ def testCoreToken_SpecialFormat(mockGUI): tokens._isFirst = True tokens._text = ( "# Title One\n\n" - "[NEW PAGE]\n\n" + "[new page]\n\n" "# Title Two\n\n" ) tokens.tokenizeText() @@ -809,7 +809,7 @@ def testCoreToken_SpecialFormat(mockGUI): tokens._isFirst = True tokens._text = ( "# Title One\n\n" - "[NEW PAGE] \t\n\n" + "[new page] \t\n\n" "# Title Two\n\n" ) tokens.tokenizeText() @@ -820,7 +820,7 @@ def testCoreToken_SpecialFormat(mockGUI): tokens._text = ( "# Title One\n\n" - "[VSPACE] \n\n" + "[vspace] \n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -840,7 +840,7 @@ def testCoreToken_SpecialFormat(mockGUI): # One Skip tokens._text = ( "# Title One\n\n" - "[VSPACE:1] \n\n" + "[vspace:1] \n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -857,7 +857,7 @@ def testCoreToken_SpecialFormat(mockGUI): # Three Skips tokens._text = ( "# Title One\n\n" - "[VSPACE:3] \n\n" + "[vspace:3] \n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -876,7 +876,7 @@ def testCoreToken_SpecialFormat(mockGUI): # Malformed Command, Case 1 tokens._text = ( "# Title One\n\n" - "[VSPACE:3xa] \n\n" + "[vspace:3xa] \n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -892,7 +892,7 @@ def testCoreToken_SpecialFormat(mockGUI): # Malformed Command, Case 2 tokens._text = ( "# Title One\n\n" - "[VSPACE:3.5]\n\n" + "[vspace:3.5]\n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -908,7 +908,7 @@ def testCoreToken_SpecialFormat(mockGUI): # Malformed Command, Case 3 tokens._text = ( "# Title One\n\n" - "[VSPACE:-1]\n\n" + "[vspace:-1]\n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -927,8 +927,8 @@ def testCoreToken_SpecialFormat(mockGUI): # Single Skip tokens._text = ( "# Title One\n\n" - "[NEW PAGE]\n\n" - "[VSPACE]\n\n" + "[new page]\n\n" + "[vspace]\n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() @@ -946,8 +946,8 @@ def testCoreToken_SpecialFormat(mockGUI): # Multiple Skip tokens._text = ( "# Title One\n\n" - "[NEW PAGE]\n\n" - "[VSPACE:3]\n\n" + "[new page]\n\n" + "[vspace:3]\n\n" "Some text to go here ...\n\n" ) tokens.tokenizeText() diff --git a/tests/test_gui/test_gui_mainmenu.py b/tests/test_gui/test_gui_mainmenu.py index 5fe1ae495..e66e5dc61 100644 --- a/tests/test_gui/test_gui_mainmenu.py +++ b/tests/test_gui/test_gui_mainmenu.py @@ -600,15 +600,15 @@ def testGuiMenu_Insert(qtbot, monkeypatch, nwGUI, fncPath, projPath, mockRnd): nwGUI.docEditor.setPlainText("### Stuff\n") nwGUI.mainMenu.aInsNewPage.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "[NEW PAGE]\n### Stuff\n" + assert nwGUI.docEditor.getText() == "[newpage]\n### Stuff\n" nwGUI.docEditor.setPlainText("### Stuff\n") nwGUI.mainMenu.aInsVSpaceS.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "[VSPACE]\n### Stuff\n" + assert nwGUI.docEditor.getText() == "[vspace]\n### Stuff\n" nwGUI.docEditor.setPlainText("### Stuff\n") nwGUI.mainMenu.aInsVSpaceM.activate(QAction.Trigger) - assert nwGUI.docEditor.getText() == "[VSPACE:2]\n### Stuff\n" + assert nwGUI.docEditor.getText() == "[vspace:2]\n### Stuff\n" nwGUI.docEditor.clear()