Use stdlib re for dialogue matching

This commit is contained in:
Veronica Berglyd Olsen
2024-09-22 17:34:04 +02:00
parent 9c63f621a8
commit c08033d153
4 changed files with 33 additions and 71 deletions
+5 -7
View File
@@ -33,7 +33,7 @@ from functools import partial
from pathlib import Path from pathlib import Path
from time import time from time import time
from PyQt5.QtCore import QCoreApplication, QRegularExpression from PyQt5.QtCore import QCoreApplication
from PyQt5.QtGui import QFont from PyQt5.QtGui import QFont
from novelwriter import CONFIG from novelwriter import CONFIG
@@ -234,7 +234,7 @@ class Tokenizer(ABC):
nwShortcode.FOOTNOTE_B: self.FMT_FNOTE, nwShortcode.FOOTNOTE_B: self.FMT_FNOTE,
} }
self._rxDialogue: list[tuple[QRegularExpression, int, int]] = [] self._rxDialogue: list[tuple[re.Pattern, int, int]] = []
return return
@@ -1136,11 +1136,9 @@ class Tokenizer(ABC):
# Match Dialogue # Match Dialogue
if self._rxDialogue and hDialog: if self._rxDialogue and hDialog:
for regEx, fmtB, fmtE in self._rxDialogue: for regEx, fmtB, fmtE in self._rxDialogue:
rxItt = regEx.globalMatch(text, 0) for match in re.finditer(regEx, text):
while rxItt.hasNext(): temp.append((match.start(0), 0, fmtB, ""))
rxMatch = rxItt.next() temp.append((match.end(0), 0, fmtE, ""))
temp.append((rxMatch.capturedStart(0), 0, fmtB, ""))
temp.append((rxMatch.capturedEnd(0), 0, fmtE, ""))
# Post-process text and format # Post-process text and format
result = text result = text
+10 -24
View File
@@ -29,7 +29,7 @@ import re
from time import time from time import time
from PyQt5.QtCore import QRegularExpression, Qt from PyQt5.QtCore import Qt
from PyQt5.QtGui import ( from PyQt5.QtGui import (
QBrush, QColor, QFont, QSyntaxHighlighter, QTextBlockUserData, QBrush, QColor, QFont, QSyntaxHighlighter, QTextBlockUserData,
QTextCharFormat, QTextDocument QTextCharFormat, QTextDocument
@@ -398,29 +398,15 @@ class GuiDocHighlighter(QSyntaxHighlighter):
if hRules: if hRules:
for rX, hRule in hRules: for rX, hRule in hRules:
if isinstance(rX, QRegularExpression): for match in re.finditer(rX, text[xOff:]):
rxItt = rX.globalMatch(text, xOff) for xM, hFmt in hRule.items():
while rxItt.hasNext(): xPos = match.start(xM) + xOff
rxMatch = rxItt.next() xEnd = match.end(xM) + xOff
for xM, hFmt in hRule.items(): for x in range(xPos, xEnd):
xPos = rxMatch.capturedStart(xM) cFmt = self.format(x)
xEnd = rxMatch.capturedEnd(xM) if cFmt.fontStyleName() != "markup":
for x in range(xPos, xEnd): cFmt.merge(hFmt)
cFmt = self.format(x) self.setFormat(x, 1, cFmt)
if cFmt.fontStyleName() != "markup":
cFmt.merge(hFmt)
self.setFormat(x, 1, cFmt)
else:
for match in re.finditer(rX, text[xOff:]):
for xM, hFmt in hRule.items():
# print(f"'{match.group(xM)}'", match.start(xM), match.end(xM))
xPos = match.start(xM) + xOff
xEnd = match.end(xM) + xOff
for x in range(xPos, xEnd):
cFmt = self.format(x)
if cFmt.fontStyleName() != "markup":
cFmt.merge(hFmt)
self.setFormat(x, 1, cFmt)
data = self.currentBlockUserData() data = self.currentBlockUserData()
if not isinstance(data, TextBlockData): if not isinstance(data, TextBlockData):
+12 -23
View File
@@ -25,11 +25,8 @@ from __future__ import annotations
import re import re
from PyQt5.QtCore import QRegularExpression
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwRegEx from novelwriter.constants import nwRegEx
from novelwriter.types import QRegExUnicode
class RegExPatterns: class RegExPatterns:
@@ -67,7 +64,7 @@ class RegExPatterns:
return self._rxSCValue return self._rxSCValue
@property @property
def dialogStyle(self) -> QRegularExpression: def dialogStyle(self) -> re.Pattern:
"""Dialogue detection rule based on user settings.""" """Dialogue detection rule based on user settings."""
symO = "" symO = ""
symC = "" symC = ""
@@ -79,34 +76,26 @@ class RegExPatterns:
symC += CONFIG.fmtDQuoteClose symC += CONFIG.fmtDQuoteClose
rxEnd = "|$" if CONFIG.allowOpenDial else "" rxEnd = "|$" if CONFIG.allowOpenDial else ""
rxRule = QRegularExpression(f"\\B[{symO}].*?(?:[{symC}]\\B{rxEnd})") return re.compile(f"\\B[{symO}].*?(?:[{symC}]\\B{rxEnd})", re.UNICODE)
rxRule.setPatternOptions(QRegExUnicode)
return rxRule
@property @property
def dialogLine(self) -> QRegularExpression: def dialogLine(self) -> re.Pattern:
"""Dialogue line rule based on user settings.""" """Dialogue line rule based on user settings."""
sym = QRegularExpression.escape(CONFIG.dialogLine) sym = re.escape(CONFIG.dialogLine)
rxRule = QRegularExpression(f"^{sym}.*?$") return re.compile(f"^{sym}.*?$", re.UNICODE)
rxRule.setPatternOptions(QRegExUnicode)
return rxRule
@property @property
def narratorBreak(self) -> QRegularExpression: def narratorBreak(self) -> re.Pattern:
"""Dialogue narrator break rule based on user settings.""" """Dialogue narrator break rule based on user settings."""
sym = QRegularExpression.escape(CONFIG.narratorBreak) sym = re.escape(CONFIG.narratorBreak)
rxRule = QRegularExpression(f"\\B{sym}\\S.*?\\S{sym}\\B") return re.compile(f"\\B{sym}\\S.*?\\S{sym}\\B", re.UNICODE)
rxRule.setPatternOptions(QRegExUnicode)
return rxRule
@property @property
def altDialogStyle(self) -> QRegularExpression: def altDialogStyle(self) -> re.Pattern:
"""Dialogue alternative rule based on user settings.""" """Dialogue alternative rule based on user settings."""
symO = QRegularExpression.escape(CONFIG.altDialogOpen) symO = re.escape(CONFIG.altDialogOpen)
symC = QRegularExpression.escape(CONFIG.altDialogClose) symC = re.escape(CONFIG.altDialogClose)
rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B") return re.compile(f"\\B{symO}.*?{symC}\\B", re.UNICODE)
rxRule.setPatternOptions(QRegExUnicode)
return rxRule
REGEX_PATTERNS = RegExPatterns() REGEX_PATTERNS = RegExPatterns()
+6 -17
View File
@@ -24,30 +24,19 @@ import re
import pytest import pytest
from PyQt5.QtCore import QRegularExpression
from novelwriter import CONFIG from novelwriter import CONFIG
from novelwriter.constants import nwUnicode from novelwriter.constants import nwUnicode
from novelwriter.text.patterns import REGEX_PATTERNS from novelwriter.text.patterns import REGEX_PATTERNS
def allMatches(regEx: QRegularExpression, text: str) -> list[list[str]]: def allMatches(regEx: re.Pattern, text: str) -> list[list[str]]:
"""Get all matches for a regex.""" """Get all matches for a regex."""
result = [] result = []
if isinstance(regEx, QRegularExpression): for match in re.finditer(regEx, text):
itt = regEx.globalMatch(text, 0) result.append([
while itt.hasNext(): (match.group(n), match.start(n), match.end(n))
match = itt.next() for n in range((match.lastindex or 0) + 1)
result.append([ ])
(match.captured(n), match.capturedStart(n), match.capturedEnd(n))
for n in range(match.lastCapturedIndex() + 1)
])
else:
for match in re.finditer(regEx, text):
result.append([
(match.group(n), match.start(n), match.end(n))
for n in range((match.lastindex or -1) + 1)
])
return result return result