Move comment processing to a separate file

This commit is contained in:
Veronica Berglyd Olsen
2025-02-09 15:39:48 +01:00
parent 9bae8f762f
commit 05feaf7d2d
6 changed files with 182 additions and 132 deletions
+1 -45
View File
@@ -43,6 +43,7 @@ from novelwriter.common import (
from novelwriter.constants import nwFiles, nwKeyWords, nwStyles
from novelwriter.enum import nwComment, nwItemClass, nwItemLayout, nwItemType
from novelwriter.error import logException
from novelwriter.text.comments import processComment
from novelwriter.text.counting import standardCounter
if TYPE_CHECKING: # pragma: no cover
@@ -1343,48 +1344,3 @@ class IndexHeading:
else:
raise ValueError("The itemIndex contains an invalid reference type")
return
# Text Processing Functions
# =========================
MODIFIERS = {
"synopsis": nwComment.SYNOPSIS,
"short": nwComment.SHORT,
"note": nwComment.NOTE,
"footnote": nwComment.FOOTNOTE,
}
KEY_REQ = {
"synopsis": 0, # Key not allowed
"short": 0, # Key not allowed
"note": 1, # Key optional
"footnote": 2, # Key required
}
def _checkModKey(modifier: str, key: str) -> bool:
"""Check if a modifier and key set are ok."""
if modifier in MODIFIERS:
if key == "":
return KEY_REQ[modifier] < 2
elif key.replace("_", "").isalnum():
return KEY_REQ[modifier] > 0
return False
def processComment(text: str) -> tuple[nwComment, str, str, int, int]:
"""Extract comment style, key and text. Should only be called on
text starting with a %.
"""
if text[:2] == "%~":
return nwComment.IGNORE, "", text[2:].lstrip(), 0, 0
check = text[1:].strip()
start, _, content = check.partition(":")
modifier, _, key = start.rstrip().partition(".")
if content and (clean := modifier.lower()) and _checkModKey(clean, key):
col = text.find(":") + 1
dot = text.find(".", 0, col) + 1
return MODIFIERS[clean], key, content.lstrip(), dot, col
return nwComment.PLAIN, "", check, 0, 0
+1 -1
View File
@@ -40,12 +40,12 @@ from novelwriter.constants import (
nwHeadFmt, nwKeyWords, nwLabels, nwShortcode, nwStats, nwStyles, nwUnicode,
trConst
)
from novelwriter.core.index import processComment
from novelwriter.core.project import NWProject
from novelwriter.enum import nwComment, nwItemLayout
from novelwriter.formats.shared import (
BlockFmt, BlockTyp, T_Block, T_Formats, T_Note, TextDocumentTheme, TextFmt
)
from novelwriter.text.comments import processComment
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
logger = logging.getLogger(__name__)
+1 -1
View File
@@ -38,8 +38,8 @@ from PyQt6.QtGui import (
from novelwriter import CONFIG, SHARED
from novelwriter.common import checkInt
from novelwriter.constants import nwStyles, nwUnicode
from novelwriter.core.index import processComment
from novelwriter.enum import nwComment
from novelwriter.text.comments import processComment
from novelwriter.text.patterns import REGEX_PATTERNS, DialogParser
logger = logging.getLogger(__name__)
+68
View File
@@ -0,0 +1,68 @@
"""
novelWriter Text Comments
===========================
File History:
Created: 2023-11-23 [2.2b1]
Moved: 2025-02-09 [2.7b1]
This file is a part of novelWriter
Copyright (C) 2025 Veronica Berglyd Olsen and novelWriter contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
from novelwriter.enum import nwComment
MODIFIERS = {
"synopsis": nwComment.SYNOPSIS,
"short": nwComment.SHORT,
"note": nwComment.NOTE,
"footnote": nwComment.FOOTNOTE,
}
KEY_REQ = {
"synopsis": 0, # Key not allowed
"short": 0, # Key not allowed
"note": 1, # Key optional
"footnote": 2, # Key required
}
def _checkModKey(modifier: str, key: str) -> bool:
"""Check if a modifier and key set are ok."""
if modifier in MODIFIERS:
if key == "":
return KEY_REQ[modifier] < 2
elif key.replace("_", "").isalnum():
return KEY_REQ[modifier] > 0
return False
def processComment(text: str) -> tuple[nwComment, str, str, int, int]:
"""Extract comment style, key and text. Should only be called on
text starting with a %.
"""
if text[:2] == "%~":
return nwComment.IGNORE, "", text[2:].lstrip(), 0, 0
check = text[1:].strip()
start, _, content = check.partition(":")
modifier, _, key = start.rstrip().partition(".")
if content and (clean := modifier.lower()) and _checkModKey(clean, key):
col = text.find(":") + 1
dot = text.find(".", 0, col) + 1
return MODIFIERS[clean], key, content.lstrip(), dot, col
return nwComment.PLAIN, "", check, 0, 0
+1 -85
View File
@@ -28,7 +28,7 @@ import pytest
from novelwriter import SHARED
from novelwriter.constants import nwFiles
from novelwriter.core.index import IndexItem, NWIndex, TagsIndex, _checkModKey, processComment
from novelwriter.core.index import IndexItem, NWIndex, TagsIndex
from novelwriter.core.item import NWItem
from novelwriter.core.project import NWProject
from novelwriter.enum import nwComment, nwItemClass, nwItemLayout
@@ -1419,87 +1419,3 @@ def testCoreIndex_ItemIndex(mockGUI, fncPath, mockRnd):
"notes": {"footnotes": ["fkey"], "comments": ["ckey"]},
}
})
@pytest.mark.core
def testCoreIndex_checkModKey():
"""Test the _checkModKey function."""
# Check Requirements
# Synopsis
assert _checkModKey("synopsis", "") is True
assert _checkModKey("synopsis", "a") is False
# Short
assert _checkModKey("short", "") is True
assert _checkModKey("short", "a") is False
# Note
assert _checkModKey("note", "") is True
assert _checkModKey("note", "a") is True
# Footnote
assert _checkModKey("footnote", "") is False
assert _checkModKey("footnote", "a") is True
# Invalid
assert _checkModKey("stuff", "") is False
assert _checkModKey("stuff", "a") is False
# Check Keys
assert _checkModKey("note", "a") is True
assert _checkModKey("note", "a1") is True
assert _checkModKey("note", "a1.2") is False
assert _checkModKey("note", "a1_2") is True
@pytest.mark.core
def testCoreIndex_processComment():
"""Test the comment processing function."""
# Plain
assert processComment("%Hi") == (nwComment.PLAIN, "", "Hi", 0, 0)
assert processComment("% Hi") == (nwComment.PLAIN, "", "Hi", 0, 0)
assert processComment("% Hi:You") == (nwComment.PLAIN, "", "Hi:You", 0, 0)
assert processComment("% Hi.You:There") == (nwComment.PLAIN, "", "Hi.You:There", 0, 0)
# Ignore
assert processComment("%~Hi") == (nwComment.IGNORE, "", "Hi", 0, 0)
assert processComment("%~ Hi") == (nwComment.IGNORE, "", "Hi", 0, 0)
# Invalid
assert processComment("") == (nwComment.PLAIN, "", "", 0, 0)
# Short : Term not allowed
assert processComment("%short: Hi") == (nwComment.SHORT, "", "Hi", 0, 7)
assert processComment("%short.a: Hi") == (nwComment.PLAIN, "", "short.a: Hi", 0, 0)
# Synopsis : Term not allowed
assert processComment("%synopsis: Hi") == (nwComment.SYNOPSIS, "", "Hi", 0, 10)
assert processComment("%synopsis.a: Hi") == (nwComment.PLAIN, "", "synopsis.a: Hi", 0, 0)
# Note : Term optional
assert processComment("%note: Hi") == (nwComment.NOTE, "", "Hi", 0, 6)
assert processComment("%note.a: Hi") == (nwComment.NOTE, "a", "Hi", 6, 8)
# Footnote : Term required
assert processComment("%footnote: Hi") == (nwComment.PLAIN, "", "footnote: Hi", 0, 0)
assert processComment("%footnote.a: Hi") == (nwComment.FOOTNOTE, "a", "Hi", 10, 12)
# Check Case
assert processComment("%Footnote.a: Hi") == (nwComment.FOOTNOTE, "a", "Hi", 10, 12)
assert processComment("%FOOTNOTE.A: Hi") == (nwComment.FOOTNOTE, "A", "Hi", 10, 12)
assert processComment("%FootNote.A_a: Hi") == (nwComment.FOOTNOTE, "A_a", "Hi", 10, 14)
# Padding without term
assert processComment("%short: Hi") == (nwComment.SHORT, "", "Hi", 0, 7)
assert processComment("% short: Hi") == (nwComment.SHORT, "", "Hi", 0, 8)
assert processComment("% short : Hi") == (nwComment.SHORT, "", "Hi", 0, 10)
assert processComment("% short : Hi") == (nwComment.SHORT, "", "Hi", 0, 12)
assert processComment("% \t short : Hi") == (nwComment.SHORT, "", "Hi", 0, 13)
# Padding with term
assert processComment("%note.term: Hi") == (nwComment.NOTE, "term", "Hi", 6, 11)
assert processComment("% note.term: Hi") == (nwComment.NOTE, "term", "Hi", 7, 12)
assert processComment("% note.term : Hi") == (nwComment.NOTE, "term", "Hi", 7, 13)
assert processComment("% note. term : Hi") == (nwComment.PLAIN, "", "note. term : Hi", 0, 0)
assert processComment("% note . term : Hi") == (nwComment.PLAIN, "", "note . term : Hi", 0, 0)
+110
View File
@@ -0,0 +1,110 @@
"""
novelWriter NWIndex Class Tester
==================================
This file is a part of novelWriter
Copyright (C) 2020 Veronica Berglyd Olsen and novelWriter contributors
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
"""
from __future__ import annotations
import pytest
from novelwriter.enum import nwComment
from novelwriter.text.comments import _checkModKey, processComment
@pytest.mark.core
def testTextComments_checkModKey():
"""Test the _checkModKey function."""
# Check Requirements
# Synopsis
assert _checkModKey("synopsis", "") is True
assert _checkModKey("synopsis", "a") is False
# Short
assert _checkModKey("short", "") is True
assert _checkModKey("short", "a") is False
# Note
assert _checkModKey("note", "") is True
assert _checkModKey("note", "a") is True
# Footnote
assert _checkModKey("footnote", "") is False
assert _checkModKey("footnote", "a") is True
# Invalid
assert _checkModKey("stuff", "") is False
assert _checkModKey("stuff", "a") is False
# Check Keys
assert _checkModKey("note", "a") is True
assert _checkModKey("note", "a1") is True
assert _checkModKey("note", "a1.2") is False
assert _checkModKey("note", "a1_2") is True
@pytest.mark.core
def testTextComments_processComment():
"""Test the comment processing function."""
# Plain
assert processComment("%Hi") == (nwComment.PLAIN, "", "Hi", 0, 0)
assert processComment("% Hi") == (nwComment.PLAIN, "", "Hi", 0, 0)
assert processComment("% Hi:You") == (nwComment.PLAIN, "", "Hi:You", 0, 0)
assert processComment("% Hi.You:There") == (nwComment.PLAIN, "", "Hi.You:There", 0, 0)
# Ignore
assert processComment("%~Hi") == (nwComment.IGNORE, "", "Hi", 0, 0)
assert processComment("%~ Hi") == (nwComment.IGNORE, "", "Hi", 0, 0)
# Invalid
assert processComment("") == (nwComment.PLAIN, "", "", 0, 0)
# Short : Term not allowed
assert processComment("%short: Hi") == (nwComment.SHORT, "", "Hi", 0, 7)
assert processComment("%short.a: Hi") == (nwComment.PLAIN, "", "short.a: Hi", 0, 0)
# Synopsis : Term not allowed
assert processComment("%synopsis: Hi") == (nwComment.SYNOPSIS, "", "Hi", 0, 10)
assert processComment("%synopsis.a: Hi") == (nwComment.PLAIN, "", "synopsis.a: Hi", 0, 0)
# Note : Term optional
assert processComment("%note: Hi") == (nwComment.NOTE, "", "Hi", 0, 6)
assert processComment("%note.a: Hi") == (nwComment.NOTE, "a", "Hi", 6, 8)
# Footnote : Term required
assert processComment("%footnote: Hi") == (nwComment.PLAIN, "", "footnote: Hi", 0, 0)
assert processComment("%footnote.a: Hi") == (nwComment.FOOTNOTE, "a", "Hi", 10, 12)
# Check Case
assert processComment("%Footnote.a: Hi") == (nwComment.FOOTNOTE, "a", "Hi", 10, 12)
assert processComment("%FOOTNOTE.A: Hi") == (nwComment.FOOTNOTE, "A", "Hi", 10, 12)
assert processComment("%FootNote.A_a: Hi") == (nwComment.FOOTNOTE, "A_a", "Hi", 10, 14)
# Padding without term
assert processComment("%short: Hi") == (nwComment.SHORT, "", "Hi", 0, 7)
assert processComment("% short: Hi") == (nwComment.SHORT, "", "Hi", 0, 8)
assert processComment("% short : Hi") == (nwComment.SHORT, "", "Hi", 0, 10)
assert processComment("% short : Hi") == (nwComment.SHORT, "", "Hi", 0, 12)
assert processComment("% \t short : Hi") == (nwComment.SHORT, "", "Hi", 0, 13)
# Padding with term
assert processComment("%note.term: Hi") == (nwComment.NOTE, "term", "Hi", 6, 11)
assert processComment("% note.term: Hi") == (nwComment.NOTE, "term", "Hi", 7, 12)
assert processComment("% note.term : Hi") == (nwComment.NOTE, "term", "Hi", 7, 13)
assert processComment("% note. term : Hi") == (nwComment.PLAIN, "", "note. term : Hi", 0, 0)
assert processComment("% note . term : Hi") == (nwComment.PLAIN, "", "note . term : Hi", 0, 0)