Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix bugs in the document viewer #2014

Merged
merged 3 commits into from
Sep 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions novelwriter/core/tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -832,7 +832,7 @@ def tokenizeText(self) -> None:
sAlign |= self.A_IND_R

# Process formats
tLine, tFmt = self._extractFormats(aLine)
tLine, tFmt = self._extractFormats(aLine, hDialog=self._isNovel)
tokens.append((
self.T_TEXT, nHead, tLine, tFmt, sAlign
))
Expand Down Expand Up @@ -1098,8 +1098,13 @@ def saveRawMarkdownJSON(self, path: str | Path) -> None:
# Internal Functions
##

def _extractFormats(self, text: str, skip: int = 0) -> tuple[str, T_Formats]:
"""Extract format markers from a text paragraph."""
def _extractFormats(
self, text: str, skip: int = 0, hDialog: bool = False
) -> tuple[str, T_Formats]:
"""Extract format markers from a text paragraph. In order to
also process dialogue highlighting, the hDialog flag must be set
to True. See issues #2011 and #2013.
"""
temp: list[tuple[int, int, int, str]] = []

# Match Markdown
Expand Down Expand Up @@ -1137,7 +1142,7 @@ def _extractFormats(self, text: str, skip: int = 0) -> tuple[str, T_Formats]:
))

# Match Dialogue
if self._rxDialogue:
if self._rxDialogue and hDialog:
for regEx, fmtB, fmtE in self._rxDialogue:
rxItt = regEx.globalMatch(text, 0)
while rxItt.hasNext():
Expand All @@ -1150,8 +1155,9 @@ def _extractFormats(self, text: str, skip: int = 0) -> tuple[str, T_Formats]:
formats = []
for pos, n, fmt, key in reversed(sorted(temp, key=lambda x: x[0])):
if fmt > 0:
result = result[:pos] + result[pos+n:]
formats = [(p-n, f, k) for p, f, k in formats]
if n > 0:
result = result[:pos] + result[pos+n:]
formats = [(p-n if p > pos else p, f, k) for p, f, k in formats]
formats.insert(0, (pos, fmt, key))

return result, formats
Expand Down
6 changes: 2 additions & 4 deletions novelwriter/gui/docviewer.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,15 +229,13 @@ def loadText(self, tHandle: str, updateHistory: bool = True) -> bool:
QApplication.restoreOverrideCursor()
return False

# Refresh the tab stops
self.setTabStopDistance(CONFIG.getTabWidth())

# Must be before setHtml
# Must be before setDocument
if updateHistory:
self.docHistory.append(tHandle)

self.setDocumentTitle(tHandle)
self.setDocument(qDoc.document)
self.setTabStopDistance(CONFIG.getTabWidth())

if self._docHandle == tHandle:
# This is a refresh, so we set the scrollbar back to where it was
Expand Down
1 change: 1 addition & 0 deletions novelwriter/tools/manuscript.py
Original file line number Diff line number Diff line change
Expand Up @@ -823,6 +823,7 @@ def setContent(self, document: QTextDocument) -> None:

document.setDocumentMargin(CONFIG.getTextMargin())
self.setDocument(document)
self.setTabStopDistance(CONFIG.getTabWidth())

self._docTime = int(time())
self._updateBuildAge()
Expand Down
19 changes: 19 additions & 0 deletions tests/test_core/test_core_tokenizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1100,6 +1100,7 @@ def testCoreToken_Dialogue(mockGUI):
project = NWProject()
tokens = BareTokenizer(project)
tokens.setDialogueHighlight(True)
tokens._isNovel = True

# Single quotes
tokens._text = "Text with \u2018dialogue one,\u2019 and \u2018dialogue two.\u2019\n"
Expand Down Expand Up @@ -1161,6 +1162,24 @@ def testCoreToken_Dialogue(mockGUI):
Tokenizer.A_NONE
)]

# Special Cases
# =============

# Dialogue + formatting on same index (Issue #2012)
tokens._text = "[i]\u201cDialogue text.\u201d[/i]\n"
tokens.tokenizeText()
assert tokens._tokens == [(
Tokenizer.T_TEXT, 0,
"\u201cDialogue text.\u201d",
[
(0, Tokenizer.FMT_I_B, ""),
(0, Tokenizer.FMT_DL_B, ""),
(16, Tokenizer.FMT_I_E, ""),
(16, Tokenizer.FMT_DL_E, ""),
],
Tokenizer.A_NONE
)]


@pytest.mark.core
def testCoreToken_SpecialFormat(mockGUI):
Expand Down