Update tests and word counter
This commit is contained in:
@@ -23,6 +23,8 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
"""
|
||||
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"(?<![\w\\])(_)(?![\s_])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_EB = r"(?<![\w\\])([\*]{2})(?![\s\*])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_ST = r"(?<![\w\\])([~]{2})(?![\s~])(.+?)(?<![\s\\])(\1)(?!\w)"
|
||||
FMT_SC = r"(?<!\\)(\[[\/\!]?(?i)(?:i|b|s|u|sup|sub)\])"
|
||||
FMT_SC = r"(?i)(?<!\\)(\[[\/\!]?(?:i|b|s|u|sup|sub)\])"
|
||||
FMT_SV = r"(?<!\\)(\[(?i)(?:fn|footnote):)(.+?)(?<!\\)(\])"
|
||||
|
||||
# Pre-Compiled RegEx
|
||||
RX_SC = re.compile(FMT_SC)
|
||||
|
||||
# END Class nwRegEx
|
||||
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ from pathlib import Path
|
||||
from novelwriter.enum import nwItemClass, nwItemType, nwItemLayout
|
||||
from novelwriter.error import logException
|
||||
from novelwriter.common import checkInt, isHandle, isItemClass, isTitleTag, jsonEncode
|
||||
from novelwriter.constants import nwFiles, nwKeyWords, nwUnicode, nwHeaders
|
||||
from novelwriter.constants import nwFiles, nwKeyWords, nwRegEx, nwUnicode, nwHeaders
|
||||
|
||||
if TYPE_CHECKING: # pragma: no cover
|
||||
from novelwriter.core.item import NWItem
|
||||
@@ -1229,6 +1229,10 @@ def countWords(text: str) -> 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] == "#":
|
||||
|
||||
@@ -55,6 +55,7 @@ from novelwriter.enum import nwDocAction, nwDocInsert, nwDocMode, nwItemClass
|
||||
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 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
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 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
"""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(
|
||||
"<br>".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(
|
||||
"<br>".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 @@ class GuiDocEditor(QPlainTextEdit):
|
||||
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:
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-10-20 22:58:21">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1605" autoCount="254" editTime="80970">
|
||||
<novelWriterXML appVersion="2.2-alpha1" hexVersion="0x020200a1" fileVersion="1.5" fileRevision="1" timeStamp="2023-11-06 11:44:21">
|
||||
<project id="e2be99af-f9bf-4403-857a-c3d1ac25abea" saveCount="1609" autoCount="255" editTime="81165">
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
@@ -36,7 +36,7 @@
|
||||
<entry key="i56be10" count="1" red="117" green="0" blue="175">Main</entry>
|
||||
</importance>
|
||||
</settings>
|
||||
<content items="27" novelWords="988" notesWords="409">
|
||||
<content items="27" novelWords="989" notesWords="409">
|
||||
<item handle="7031beac91f75" parent="None" root="7031beac91f75" order="0" type="ROOT" class="NOVEL">
|
||||
<meta expanded="yes" />
|
||||
<name status="sc24b8f" import="ia857f0">Novel</name>
|
||||
@@ -46,7 +46,7 @@
|
||||
<name status="sc24b8f" import="ia857f0" active="yes">Title Page</name>
|
||||
</item>
|
||||
<item handle="974e400180a99" parent="7031beac91f75" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H0" charCount="251" wordCount="50" paraCount="2" cursorPos="10" />
|
||||
<meta expanded="no" heading="H0" charCount="269" wordCount="51" paraCount="3" cursorPos="275" />
|
||||
<name status="sf12341" import="ia857f0" active="yes">Page</name>
|
||||
</item>
|
||||
<item handle="edca4be2fcaf8" parent="7031beac91f75" root="7031beac91f75" order="2" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
@@ -58,7 +58,7 @@
|
||||
<name status="sf24ce6" import="ia857f0" active="yes">Chapter One</name>
|
||||
</item>
|
||||
<item handle="636b6aa9b697b" parent="6a2d6d5f4f401" root="7031beac91f75" order="0" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
<meta expanded="no" heading="H3" charCount="2937" wordCount="513" paraCount="15" cursorPos="19" />
|
||||
<meta expanded="no" heading="H3" charCount="2901" wordCount="513" paraCount="15" cursorPos="749" />
|
||||
<name status="s90e6c9" import="ia857f0" active="yes">Making a Scene</name>
|
||||
</item>
|
||||
<item handle="bc0cbd2a407f3" parent="6a2d6d5f4f401" root="7031beac91f75" order="1" type="FILE" class="NOVEL" layout="DOCUMENT">
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user