From 34a3e173269a54d7653794b2e527d0535a8ab67f Mon Sep 17 00:00:00 2001 From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com> Date: Sun, 3 Nov 2024 22:09:16 +0100 Subject: [PATCH] Fix quotes regex issue and update tests --- novelwriter/text/patterns.py | 4 +- tests/reference/baseConfig_novelwriter.conf | 5 +- tests/test_dialogs/test_dlg_preferences.py | 9 ++- tests/test_text/test_text_patterns.py | 78 ++++++++++++++++++--- 4 files changed, 80 insertions(+), 16 deletions(-) diff --git a/novelwriter/text/patterns.py b/novelwriter/text/patterns.py index 6bfa6d9d..e2137a4d 100644 --- a/novelwriter/text/patterns.py +++ b/novelwriter/text/patterns.py @@ -91,11 +91,11 @@ class RegExPatterns: if CONFIG.dialogStyle in (1, 3): qO = CONFIG.fmtSQuoteOpen.strip()[:1] qC = CONFIG.fmtSQuoteClose.strip()[:1] - rx.append(f"(\\B{qO}.*?(?:{qC}\\B{end}))") + rx.append(f"(?:\\B{qO}.*?(?:{qC}\\B{end}))") if CONFIG.dialogStyle in (2, 3): qO = CONFIG.fmtDQuoteOpen.strip()[:1] qC = CONFIG.fmtDQuoteClose.strip()[:1] - rx.append(f"(\\B{qO}.*?(?:{qC}\\B{end}))") + rx.append(f"(?:\\B{qO}.*?(?:{qC}\\B{end}))") return re.compile("|".join(rx), re.UNICODE) return None diff --git a/tests/reference/baseConfig_novelwriter.conf b/tests/reference/baseConfig_novelwriter.conf index b9d485a2..046ac8c8 100644 --- a/tests/reference/baseConfig_novelwriter.conf +++ b/tests/reference/baseConfig_novelwriter.conf @@ -1,5 +1,5 @@ [Meta] -timestamp = 2024-06-16 00:36:27 +timestamp = 2024-11-03 21:45:08 [Main] font = @@ -59,10 +59,11 @@ incnoteswcount = True showfullpath = True dialogstyle = 2 allowopendial = True +dialogline = narratorbreak = +narratordialog = altdialogopen = altdialogclose = -dialogline = highlightemph = True stopwhenidle = True useridletime = 300 diff --git a/tests/test_dialogs/test_dlg_preferences.py b/tests/test_dialogs/test_dlg_preferences.py index 036621df..4285da9f 100644 --- a/tests/test_dialogs/test_dlg_preferences.py +++ b/tests/test_dialogs/test_dlg_preferences.py @@ -247,8 +247,9 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths): # Text Highlighting prefs.dialogStyle.setCurrentData(3, 0) prefs.allowOpenDial.setChecked(False) - prefs.narratorBreak.setText("–") prefs.dialogLine.setText("–") + prefs.narratorBreak.setText("–") + prefs.narratorDialog.setText("–") prefs.altDialogOpen.setText("<") prefs.altDialogClose.setText(">") prefs.highlightEmph.setChecked(False) @@ -256,8 +257,9 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths): assert CONFIG.dialogStyle == 2 assert CONFIG.allowOpenDial is True - assert CONFIG.narratorBreak == "" assert CONFIG.dialogLine == "" + assert CONFIG.narratorBreak == "" + assert CONFIG.narratorDialog == "" assert CONFIG.altDialogOpen == "" assert CONFIG.altDialogClose == "" assert CONFIG.highlightEmph is True @@ -366,8 +368,9 @@ def testDlgPreferences_Settings(qtbot, monkeypatch, nwGUI, tstPaths): # Text Highlighting assert CONFIG.dialogStyle == 3 assert CONFIG.allowOpenDial is False - assert CONFIG.narratorBreak == "–" assert CONFIG.dialogLine == "–" + assert CONFIG.narratorBreak == "–" + assert CONFIG.narratorDialog == "–" assert CONFIG.altDialogOpen == "<" assert CONFIG.altDialogClose == ">" assert CONFIG.highlightEmph is False diff --git a/tests/test_text/test_text_patterns.py b/tests/test_text/test_text_patterns.py index 4c68f144..2ace18bf 100644 --- a/tests/test_text/test_text_patterns.py +++ b/tests/test_text/test_text_patterns.py @@ -296,6 +296,16 @@ def testTextPatterns_DialogueStyle(): [("\u201ctwo\u201d", 4, 9)] ] + # Both single and double quotes are recognised + assert allMatches(regEx, "one \u2018two\u2019 three \u201cfour\u201d five") == [ + [("\u2018two\u2019", 4, 9)], [("\u201cfour\u201d", 16, 22)] + ] + + # But not mixed + assert allMatches(regEx, "one \u2018two\u201d three \u201cfour\u2019 five") == [ + [("\u2018two\u201d three \u201cfour\u2019", 4, 22)] + ] + # Straight single quotes are ignored assert allMatches(regEx, "one 'two' three") == [] @@ -363,6 +373,7 @@ def testTextPatterns_DialogParserEnglish(): parser = DialogParser() parser.initParser() + assert parser.enabled is True # Positions: 0 18 assert parser("“Simple dialogue.”") == [ @@ -384,9 +395,14 @@ def testTextPatterns_DialogParserEnglish(): CONFIG.narratorBreak = nwUnicode.U_EMDASH parser.initParser() - # Positions: 0 18 34 58 + # Positions: 0 18 32 58 assert parser("“Simple dialogue, — argued John, — is not always so easy.”") == [ - (0, 18), (34, 58), + (0, 18), (32, 58), + ] + + # Positions: 0 18 32 56 + assert parser("“Simple dialogue, —argued John—, is not always so easy.”") == [ + (0, 18), (32, 56), ] @@ -422,17 +438,15 @@ def testTextPatterns_DialogParserSpanish(): @pytest.mark.core -def testTextPatterns_DialogParserAlternating(): - """Test the dialog parser with alternating dialogue/narration like - for Portuguese and Polish. - """ +def testTextPatterns_DialogParserPortuguese(): + """Test the dialog parser with Portuguese settings.""" # Set the config CONFIG.dialogStyle = 0 CONFIG.fmtSQuoteOpen = nwUnicode.U_LSAQUO CONFIG.fmtSQuoteClose = nwUnicode.U_RSAQUO CONFIG.fmtDQuoteOpen = nwUnicode.U_LAQUO CONFIG.fmtDQuoteClose = nwUnicode.U_RAQUO - CONFIG.dialogLine = "" + CONFIG.dialogLine = nwUnicode.U_EMDASH CONFIG.narratorBreak = nwUnicode.U_EMDASH parser = DialogParser() @@ -448,7 +462,53 @@ def testTextPatterns_DialogParserAlternating(): (0, 12), ] - # Positions: 0 12 28 49 + # Positions: 0 12 27 49 assert parser("— Tudo bem? — ele pergunta. — Você falou com ele?") == [ - (0, 12), (28, 49), + (0, 12), (27, 49), ] + + +@pytest.mark.core +def testTextPatterns_DialogParserPolish(): + """Test the dialog parser with alternating Polish settings.""" + # Set the config + CONFIG.dialogStyle = 0 + CONFIG.fmtSQuoteOpen = "'" + CONFIG.fmtSQuoteClose = "'" + CONFIG.fmtDQuoteOpen = '"' + CONFIG.fmtDQuoteClose = '"' + CONFIG.dialogLine = "" + CONFIG.narratorBreak = "" + CONFIG.narratorDialog = nwUnicode.U_ENDASH + + parser = DialogParser() + parser.initParser() + + # This is what an example dialogue might look like using Polish punctuation rules + # See discussion #1976 + + assert parser( + "– Example statement – someone said. And he added: – Another example statement." + ) == [ + (0, 20), (50, 78), + ] + + assert parser( + "– Oh my! – It would be nice if only the statements were highlighted, without " + "any narration. In a paragraph where there is only a short statement and then " + "a lot happens, this would be especially justified." + ) == [ + (0, 9), + ] + + assert parser( + "There are also sometimes paragraphs that start with a narrative, and only then " + "someone shouts out the words: – Oooh! Look!" + ) == [ + (109, 122), + ] + + assert parser( + "And so on and so forth. However, \"text in quotation marks\" should not be " + "highlighted at all, and if so, it should be highlighted differently." + ) == []