Restructure and improve dialogue highlighting
This commit is contained in:
@@ -114,15 +114,20 @@ REGEX_PATTERNS = RegExPatterns()
|
|||||||
|
|
||||||
class DialogParser:
|
class DialogParser:
|
||||||
|
|
||||||
__slots__ = ("_quotes", "_dialog", "_narrator", "_alternate", "_break", "_enabled")
|
__slots__ = (
|
||||||
|
"_quotes", "_dialog", "_alternate", "_enabled",
|
||||||
|
"_narrator", "_breakD", "_breakQ", "_mode",
|
||||||
|
)
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._quotes = None
|
self._quotes = None
|
||||||
self._dialog = ""
|
self._dialog = ""
|
||||||
self._narrator = ""
|
|
||||||
self._alternate = ""
|
self._alternate = ""
|
||||||
self._break = re.compile("")
|
|
||||||
self._enabled = False
|
self._enabled = False
|
||||||
|
self._narrator = ""
|
||||||
|
self._breakD = None
|
||||||
|
self._breakQ = None
|
||||||
|
self._mode = ""
|
||||||
return
|
return
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@@ -131,21 +136,31 @@ class DialogParser:
|
|||||||
return self._enabled
|
return self._enabled
|
||||||
|
|
||||||
def initParser(self) -> None:
|
def initParser(self) -> None:
|
||||||
"""Init parser settings. Must be called when config changes."""
|
"""Init parser settings. This method must also be called when
|
||||||
punct = re.escape("!?.,:;")
|
the config changes.
|
||||||
|
"""
|
||||||
self._quotes = REGEX_PATTERNS.dialogStyle
|
self._quotes = REGEX_PATTERNS.dialogStyle
|
||||||
self._dialog = uniqueCompact(CONFIG.dialogLine)
|
self._dialog = uniqueCompact(CONFIG.dialogLine)
|
||||||
self._narrator = CONFIG.narratorBreak.strip()[:1]
|
|
||||||
self._alternate = CONFIG.narratorDialog.strip()[:1]
|
self._alternate = CONFIG.narratorDialog.strip()[:1]
|
||||||
self._break = re.compile(
|
|
||||||
f"({self._narrator}\\s?.*?)(\\s?(?:{self._narrator}[{punct}]?|$))", re.UNICODE
|
# One of the three modes are needed for the class to have
|
||||||
)
|
# anything to do
|
||||||
self._enabled = bool(self._quotes or self._dialog or self._narrator or self._alternate)
|
self._enabled = bool(self._quotes or self._dialog or self._alternate)
|
||||||
|
|
||||||
|
# Build narrator break RegExes
|
||||||
|
if narrator := CONFIG.narratorBreak.strip()[:1]:
|
||||||
|
punct = re.escape(".,:;!?")
|
||||||
|
self._breakD = re.compile(f"{narrator}.*?(?:{narrator}[{punct}]?|$)", re.UNICODE)
|
||||||
|
self._breakQ = re.compile(f"{narrator}.*?(?:{narrator}[{punct}]?)", re.UNICODE)
|
||||||
|
self._narrator = narrator
|
||||||
|
self._mode = f" {narrator}"
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
def __call__(self, text: str) -> list[tuple[int, int]]:
|
def __call__(self, text: str) -> list[tuple[int, int]]:
|
||||||
"""Caller wrapper for dialogue processing."""
|
"""Caller wrapper for dialogue processing."""
|
||||||
temp: list[int] = []
|
temp: list[int] = []
|
||||||
|
result: list[tuple[int, int]] = []
|
||||||
if text:
|
if text:
|
||||||
plain = True
|
plain = True
|
||||||
if self._dialog and text[0] in self._dialog:
|
if self._dialog and text[0] in self._dialog:
|
||||||
@@ -153,44 +168,41 @@ class DialogParser:
|
|||||||
plain = False
|
plain = False
|
||||||
temp.append(0)
|
temp.append(0)
|
||||||
temp.append(len(text))
|
temp.append(len(text))
|
||||||
if self._narrator:
|
if self._breakD:
|
||||||
# Process narrator breaks in the dialogue
|
# Process narrator breaks in the dialogue
|
||||||
for res in self._break.finditer(text, 1):
|
for res in self._breakD.finditer(text, 1):
|
||||||
temp.append(res.start(0))
|
temp.append(res.start(0))
|
||||||
if (two := res.group(2)) and two[0].isspace():
|
temp.append(res.end(0))
|
||||||
temp.append(res.start(2))
|
|
||||||
else:
|
|
||||||
temp.append(res.end(0))
|
|
||||||
elif self._quotes:
|
elif self._quotes:
|
||||||
# The line contains quoted dialogue
|
# Quoted dialogue is enabled, so we look for them
|
||||||
for res in self._quotes.finditer(text):
|
for res in self._quotes.finditer(text):
|
||||||
plain = False
|
plain = False
|
||||||
temp.append(res.start(0))
|
temp.append(res.start(0))
|
||||||
temp.append(res.end(0))
|
temp.append(res.end(0))
|
||||||
if self._narrator:
|
if self._breakQ:
|
||||||
for res in self._break.finditer(text, 1):
|
for sub in self._breakQ.finditer(text, res.start(0), res.end(0)):
|
||||||
temp.append(res.start(0))
|
temp.append(sub.start(0))
|
||||||
if (two := res.group(2)) and two[0].isspace():
|
temp.append(sub.end(0))
|
||||||
temp.append(res.start(2))
|
|
||||||
else:
|
|
||||||
temp.append(res.end(0))
|
|
||||||
|
|
||||||
if plain and self._alternate:
|
if plain and self._alternate:
|
||||||
|
# The main rules found no dialogue, so we check for
|
||||||
|
# alternating dialogue sections, if enabled
|
||||||
pos = 0
|
pos = 0
|
||||||
for num, bit in enumerate(text.split(self._alternate)):
|
for num, bit in enumerate(text.split(self._alternate)):
|
||||||
length = len(bit) + int(num > 0)
|
length = len(bit) + (1 if num > 0 else 0)
|
||||||
if num%2:
|
if num%2:
|
||||||
temp.append(pos)
|
temp.append(pos)
|
||||||
temp.append(pos + length)
|
temp.append(pos + length)
|
||||||
pos += length
|
pos += length
|
||||||
|
|
||||||
start = None
|
if temp:
|
||||||
result = []
|
# Sort unique edges in increasing order, and add them in pairs
|
||||||
for pos in sorted(set(temp)):
|
|
||||||
if start is None:
|
|
||||||
start = pos
|
|
||||||
else:
|
|
||||||
result.append((start, pos))
|
|
||||||
start = None
|
start = None
|
||||||
|
for pos in sorted(set(temp)):
|
||||||
|
if start is None:
|
||||||
|
start = pos
|
||||||
|
else:
|
||||||
|
result.append((start, pos))
|
||||||
|
start = None
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|||||||
@@ -395,9 +395,9 @@ def testTextPatterns_DialogParserEnglish():
|
|||||||
CONFIG.narratorBreak = nwUnicode.U_EMDASH
|
CONFIG.narratorBreak = nwUnicode.U_EMDASH
|
||||||
parser.initParser()
|
parser.initParser()
|
||||||
|
|
||||||
# Positions: 0 18 32 58
|
# Positions: 0 18 34 58
|
||||||
assert parser("“Simple dialogue, — argued John, — is not always so easy.”") == [
|
assert parser("“Simple dialogue, — argued John, — is not always so easy.”") == [
|
||||||
(0, 18), (32, 58),
|
(0, 18), (34, 58),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Positions: 0 18 32 56
|
# Positions: 0 18 32 56
|
||||||
@@ -405,6 +405,11 @@ def testTextPatterns_DialogParserEnglish():
|
|||||||
(0, 18), (32, 56),
|
(0, 18), (32, 56),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# Positions: 0 31
|
||||||
|
assert parser("“Simple dialogue, —argued John”") == [
|
||||||
|
(0, 31),
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.core
|
@pytest.mark.core
|
||||||
def testTextPatterns_DialogParserSpanish():
|
def testTextPatterns_DialogParserSpanish():
|
||||||
@@ -462,9 +467,14 @@ def testTextPatterns_DialogParserPortuguese():
|
|||||||
(0, 12),
|
(0, 12),
|
||||||
]
|
]
|
||||||
|
|
||||||
# Positions: 0 12 27 49
|
# Positions: 0 12 29 49
|
||||||
assert parser("— Tudo bem? — ele pergunta. — Você falou com ele?") == [
|
assert parser("— Tudo bem? — ele pergunta. — Você falou com ele?") == [
|
||||||
(0, 12), (27, 49),
|
(0, 12), (29, 49),
|
||||||
|
]
|
||||||
|
|
||||||
|
# Positions: 0 12 29 49
|
||||||
|
assert parser("— Tudo bem? — ele pergunta —. Você falou com ele?") == [
|
||||||
|
(0, 12), (29, 49),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user