novelWriter 2.4.2__py3-none-any.whl → 2.5b1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/METADATA +4 -5
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/RECORD +109 -101
- novelwriter/__init__.py +33 -39
- novelwriter/assets/i18n/project_en_GB.json +1 -0
- novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
- novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
- novelwriter/assets/icons/typicons_light/icons.conf +2 -0
- novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
- novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
- novelwriter/assets/manual.pdf +0 -0
- novelwriter/assets/sample.zip +0 -0
- novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
- novelwriter/assets/syntax/default_dark.conf +32 -18
- novelwriter/assets/syntax/default_light.conf +24 -10
- novelwriter/assets/syntax/dracula.conf +44 -0
- novelwriter/assets/syntax/grey_dark.conf +5 -4
- novelwriter/assets/syntax/grey_light.conf +5 -4
- novelwriter/assets/syntax/light_owl.conf +7 -6
- novelwriter/assets/syntax/night_owl.conf +7 -6
- novelwriter/assets/syntax/snazzy.conf +42 -0
- novelwriter/assets/syntax/solarized_dark.conf +4 -3
- novelwriter/assets/syntax/solarized_light.conf +4 -3
- novelwriter/assets/syntax/tango.conf +27 -11
- novelwriter/assets/syntax/tomorrow.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night.conf +7 -6
- novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
- novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
- novelwriter/assets/text/credits_en.htm +4 -1
- novelwriter/assets/themes/cyberpunk_night.conf +2 -0
- novelwriter/assets/themes/default_dark.conf +1 -0
- novelwriter/assets/themes/default_light.conf +1 -0
- novelwriter/assets/themes/dracula.conf +47 -0
- novelwriter/assets/themes/solarized_dark.conf +1 -0
- novelwriter/assets/themes/solarized_light.conf +1 -0
- novelwriter/common.py +31 -9
- novelwriter/config.py +118 -84
- novelwriter/constants.py +40 -26
- novelwriter/core/buildsettings.py +63 -66
- novelwriter/core/coretools.py +2 -22
- novelwriter/core/docbuild.py +51 -40
- novelwriter/core/document.py +3 -5
- novelwriter/core/index.py +115 -45
- novelwriter/core/item.py +8 -19
- novelwriter/core/options.py +2 -4
- novelwriter/core/project.py +23 -57
- novelwriter/core/projectdata.py +1 -3
- novelwriter/core/projectxml.py +12 -15
- novelwriter/core/sessions.py +3 -5
- novelwriter/core/spellcheck.py +4 -9
- novelwriter/core/status.py +211 -164
- novelwriter/core/storage.py +0 -8
- novelwriter/core/tohtml.py +94 -100
- novelwriter/core/tokenizer.py +199 -112
- novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
- novelwriter/core/toodt.py +212 -148
- novelwriter/core/toqdoc.py +403 -0
- novelwriter/core/tree.py +5 -7
- novelwriter/dialogs/about.py +3 -5
- novelwriter/dialogs/docmerge.py +1 -3
- novelwriter/dialogs/docsplit.py +1 -3
- novelwriter/dialogs/editlabel.py +0 -2
- novelwriter/dialogs/preferences.py +111 -88
- novelwriter/dialogs/projectsettings.py +216 -180
- novelwriter/dialogs/quotes.py +3 -4
- novelwriter/dialogs/wordlist.py +3 -9
- novelwriter/enum.py +31 -25
- novelwriter/error.py +8 -15
- novelwriter/extensions/circularprogress.py +5 -6
- novelwriter/extensions/configlayout.py +18 -18
- novelwriter/extensions/eventfilters.py +1 -5
- novelwriter/extensions/modified.py +50 -13
- novelwriter/extensions/novelselector.py +1 -3
- novelwriter/extensions/pagedsidebar.py +9 -12
- novelwriter/extensions/simpleprogress.py +1 -3
- novelwriter/extensions/statusled.py +1 -3
- novelwriter/extensions/switch.py +4 -6
- novelwriter/extensions/switchbox.py +7 -6
- novelwriter/extensions/versioninfo.py +3 -9
- novelwriter/gui/doceditor.py +132 -133
- novelwriter/gui/dochighlight.py +237 -183
- novelwriter/gui/docviewer.py +61 -97
- novelwriter/gui/docviewerpanel.py +3 -10
- novelwriter/gui/editordocument.py +1 -3
- novelwriter/gui/itemdetails.py +7 -11
- novelwriter/gui/mainmenu.py +11 -7
- novelwriter/gui/noveltree.py +11 -24
- novelwriter/gui/outline.py +11 -23
- novelwriter/gui/projtree.py +26 -43
- novelwriter/gui/search.py +1 -3
- novelwriter/gui/sidebar.py +2 -6
- novelwriter/gui/statusbar.py +6 -10
- novelwriter/gui/theme.py +26 -51
- novelwriter/guimain.py +50 -71
- novelwriter/shared.py +30 -15
- novelwriter/tools/dictionaries.py +12 -15
- novelwriter/tools/lipsum.py +2 -4
- novelwriter/tools/manusbuild.py +1 -3
- novelwriter/tools/manuscript.py +71 -144
- novelwriter/tools/manussettings.py +67 -73
- novelwriter/tools/noveldetails.py +6 -11
- novelwriter/tools/welcome.py +2 -16
- novelwriter/tools/writingstats.py +6 -9
- novelwriter/types.py +45 -3
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/LICENSE.md +0 -0
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/WHEEL +0 -0
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/entry_points.txt +0 -0
- {novelWriter-2.4.2.dist-info → novelWriter-2.5b1.dist-info}/top_level.txt +0 -0
novelwriter/gui/dochighlight.py
CHANGED
@@ -28,26 +28,27 @@ import logging
|
|
28
28
|
|
29
29
|
from time import time
|
30
30
|
|
31
|
-
from PyQt5.QtCore import
|
31
|
+
from PyQt5.QtCore import QRegularExpression, Qt
|
32
32
|
from PyQt5.QtGui import (
|
33
33
|
QBrush, QColor, QFont, QSyntaxHighlighter, QTextBlockUserData,
|
34
34
|
QTextCharFormat, QTextDocument
|
35
35
|
)
|
36
36
|
|
37
37
|
from novelwriter import CONFIG, SHARED
|
38
|
-
from novelwriter.enum import nwComment
|
39
38
|
from novelwriter.common import checkInt
|
40
|
-
from novelwriter.constants import nwRegEx, nwUnicode
|
39
|
+
from novelwriter.constants import nwHeaders, nwRegEx, nwUnicode
|
41
40
|
from novelwriter.core.index import processComment
|
41
|
+
from novelwriter.enum import nwComment
|
42
|
+
from novelwriter.types import QRegExUnicode
|
42
43
|
|
43
44
|
logger = logging.getLogger(__name__)
|
44
45
|
|
45
46
|
SPELLRX = QRegularExpression(r"\b[^\s\-\+\/–—\[\]:]+\b")
|
46
|
-
SPELLRX.setPatternOptions(
|
47
|
+
SPELLRX.setPatternOptions(QRegExUnicode)
|
47
48
|
SPELLSC = QRegularExpression(nwRegEx.FMT_SC)
|
48
|
-
SPELLSC.setPatternOptions(
|
49
|
+
SPELLSC.setPatternOptions(QRegExUnicode)
|
49
50
|
SPELLSV = QRegularExpression(nwRegEx.FMT_SV)
|
50
|
-
SPELLSV.setPatternOptions(
|
51
|
+
SPELLSV.setPatternOptions(QRegExUnicode)
|
51
52
|
|
52
53
|
BLOCK_NONE = 0
|
53
54
|
BLOCK_TEXT = 1
|
@@ -57,8 +58,10 @@ BLOCK_TITLE = 4
|
|
57
58
|
|
58
59
|
class GuiDocHighlighter(QSyntaxHighlighter):
|
59
60
|
|
60
|
-
__slots__ = (
|
61
|
-
|
61
|
+
__slots__ = (
|
62
|
+
"_tHandle", "_isInactive", "_spellCheck", "_spellErr", "_hStyles",
|
63
|
+
"_txtRules", "_cmnRules",
|
64
|
+
)
|
62
65
|
|
63
66
|
def __init__(self, document: QTextDocument) -> None:
|
64
67
|
super().__init__(document)
|
@@ -70,9 +73,9 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
70
73
|
self._spellCheck = False
|
71
74
|
self._spellErr = QTextCharFormat()
|
72
75
|
|
73
|
-
self._hRules: list[tuple[str, dict]] = []
|
74
76
|
self._hStyles: dict[str, QTextCharFormat] = {}
|
75
|
-
self.
|
77
|
+
self._txtRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
|
78
|
+
self._cmnRules: list[tuple[QRegularExpression, dict[int, QTextCharFormat]]] = []
|
76
79
|
|
77
80
|
self.initHighlighter()
|
78
81
|
|
@@ -90,145 +93,178 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
90
93
|
colBreak = QColor(SHARED.theme.colEmph)
|
91
94
|
colBreak.setAlpha(64)
|
92
95
|
|
93
|
-
|
94
|
-
self.
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
96
|
+
# Create Character Formats
|
97
|
+
self._addCharFormat("text", SHARED.theme.colText)
|
98
|
+
self._addCharFormat("header1", SHARED.theme.colHead, "b", nwHeaders.H_SIZES[1])
|
99
|
+
self._addCharFormat("header2", SHARED.theme.colHead, "b", nwHeaders.H_SIZES[2])
|
100
|
+
self._addCharFormat("header3", SHARED.theme.colHead, "b", nwHeaders.H_SIZES[3])
|
101
|
+
self._addCharFormat("header4", SHARED.theme.colHead, "b", nwHeaders.H_SIZES[4])
|
102
|
+
self._addCharFormat("head1h", SHARED.theme.colHeadH, "b", nwHeaders.H_SIZES[1])
|
103
|
+
self._addCharFormat("head2h", SHARED.theme.colHeadH, "b", nwHeaders.H_SIZES[2])
|
104
|
+
self._addCharFormat("head3h", SHARED.theme.colHeadH, "b", nwHeaders.H_SIZES[3])
|
105
|
+
self._addCharFormat("head4h", SHARED.theme.colHeadH, "b", nwHeaders.H_SIZES[4])
|
106
|
+
self._addCharFormat("bold", colEmph, "b")
|
107
|
+
self._addCharFormat("italic", colEmph, "i")
|
108
|
+
self._addCharFormat("strike", SHARED.theme.colHidden, "s")
|
109
|
+
self._addCharFormat("mspaces", SHARED.theme.colError, "err")
|
110
|
+
self._addCharFormat("nobreak", colBreak, "bg")
|
111
|
+
self._addCharFormat("altdialog", SHARED.theme.colDialA)
|
112
|
+
self._addCharFormat("dialog", SHARED.theme.colDialN)
|
113
|
+
self._addCharFormat("replace", SHARED.theme.colRepTag)
|
114
|
+
self._addCharFormat("hidden", SHARED.theme.colHidden)
|
115
|
+
self._addCharFormat("markup", SHARED.theme.colHidden)
|
116
|
+
self._addCharFormat("note", SHARED.theme.colNote)
|
117
|
+
self._addCharFormat("code", SHARED.theme.colCode)
|
118
|
+
self._addCharFormat("keyword", SHARED.theme.colKey)
|
119
|
+
self._addCharFormat("tag", SHARED.theme.colTag)
|
120
|
+
self._addCharFormat("modifier", SHARED.theme.colMod)
|
121
|
+
self._addCharFormat("value", SHARED.theme.colVal)
|
122
|
+
self._addCharFormat("optional", SHARED.theme.colOpt)
|
123
|
+
self._addCharFormat("invalid", None, "err")
|
121
124
|
|
122
125
|
# Cache Spell Error Format
|
123
126
|
self._spellErr = QTextCharFormat()
|
124
127
|
self._spellErr.setUnderlineColor(SHARED.theme.colSpell)
|
125
128
|
self._spellErr.setUnderlineStyle(QTextCharFormat.UnderlineStyle.SpellCheckUnderline)
|
126
129
|
|
130
|
+
self._txtRules.clear()
|
131
|
+
self._cmnRules.clear()
|
132
|
+
|
127
133
|
# Multiple or Trailing Spaces
|
128
134
|
if CONFIG.showMultiSpaces:
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
135
|
+
rxRule = QRegularExpression(r"[ ]{2,}|[ ]*$")
|
136
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
137
|
+
hlRule = {
|
138
|
+
0: self._hStyles["mspaces"],
|
139
|
+
}
|
140
|
+
self._txtRules.append((rxRule, hlRule))
|
141
|
+
self._cmnRules.append((rxRule, hlRule))
|
134
142
|
|
135
143
|
# Non-Breaking Spaces
|
136
|
-
|
137
|
-
|
138
|
-
|
144
|
+
rxRule = QRegularExpression(f"[{nwUnicode.U_NBSP}{nwUnicode.U_THNBSP}]+")
|
145
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
146
|
+
hlRule = {
|
147
|
+
0: self._hStyles["nobreak"],
|
148
|
+
}
|
149
|
+
self._txtRules.append((rxRule, hlRule))
|
150
|
+
self._cmnRules.append((rxRule, hlRule))
|
151
|
+
|
152
|
+
# Dialogue
|
153
|
+
if CONFIG.dialogStyle > 0:
|
154
|
+
symO = ""
|
155
|
+
symC = ""
|
156
|
+
if CONFIG.dialogStyle in (1, 3):
|
157
|
+
symO += CONFIG.fmtSQuoteOpen
|
158
|
+
symC += CONFIG.fmtSQuoteClose
|
159
|
+
if CONFIG.dialogStyle in (2, 3):
|
160
|
+
symO += CONFIG.fmtDQuoteOpen
|
161
|
+
symC += CONFIG.fmtDQuoteClose
|
162
|
+
|
163
|
+
rxEnd = "|$" if CONFIG.allowOpenDial else ""
|
164
|
+
rxRule = QRegularExpression(f"\\B[{symO}].*?[{symC}]\\B{rxEnd}")
|
165
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
166
|
+
hlRule = {
|
167
|
+
0: self._hStyles["dialog"],
|
139
168
|
}
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
# Straight Quotes
|
150
|
-
if not (fmtDblO == fmtDblC == "\""):
|
151
|
-
self._hRules.append((
|
152
|
-
"(\\B\")(.*?)(\"\\B)", {
|
153
|
-
0: self._hStyles["dialogue1"],
|
154
|
-
}
|
155
|
-
))
|
156
|
-
|
157
|
-
# Double Quotes
|
158
|
-
dblEnd = "|$" if CONFIG.allowOpenDQuote else ""
|
159
|
-
self._hRules.append((
|
160
|
-
f"(\\B{fmtDblO})(.*?)({fmtDblC}\\B{dblEnd})", {
|
161
|
-
0: self._hStyles["dialogue2"],
|
162
|
-
}
|
163
|
-
))
|
164
|
-
|
165
|
-
# Single Quotes
|
166
|
-
sngEnd = "|$" if CONFIG.allowOpenSQuote else ""
|
167
|
-
self._hRules.append((
|
168
|
-
f"(\\B{fmtSngO})(.*?)({fmtSngC}\\B{sngEnd})", {
|
169
|
-
0: self._hStyles["dialogue3"],
|
170
|
-
}
|
171
|
-
))
|
172
|
-
|
173
|
-
# Markdown Syntax
|
174
|
-
self._hRules.append((
|
175
|
-
nwRegEx.FMT_EI, {
|
176
|
-
1: self._hStyles["hidden"],
|
177
|
-
2: self._hStyles["italic"],
|
178
|
-
3: self._hStyles["hidden"],
|
169
|
+
self._txtRules.append((rxRule, hlRule))
|
170
|
+
|
171
|
+
if CONFIG.dialogLine:
|
172
|
+
sym = QRegularExpression.escape(CONFIG.dialogLine)
|
173
|
+
rxRule = QRegularExpression(f"^{sym}.*?$")
|
174
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
175
|
+
hlRule = {
|
176
|
+
0: self._hStyles["dialog"],
|
179
177
|
}
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
178
|
+
self._txtRules.append((rxRule, hlRule))
|
179
|
+
|
180
|
+
if CONFIG.narratorBreak:
|
181
|
+
sym = QRegularExpression.escape(CONFIG.narratorBreak)
|
182
|
+
rxRule = QRegularExpression(f"({sym}\\b)(.*?)(\\b{sym})")
|
183
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
184
|
+
hlRule = {
|
185
|
+
0: self._hStyles["text"],
|
186
186
|
}
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
187
|
+
self._txtRules.append((rxRule, hlRule))
|
188
|
+
|
189
|
+
if CONFIG.altDialogOpen and CONFIG.altDialogClose:
|
190
|
+
symO = QRegularExpression.escape(CONFIG.altDialogOpen)
|
191
|
+
symC = QRegularExpression.escape(CONFIG.altDialogClose)
|
192
|
+
rxRule = QRegularExpression(f"\\B{symO}.*?{symC}\\B")
|
193
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
194
|
+
hlRule = {
|
195
|
+
0: self._hStyles["altdialog"],
|
193
196
|
}
|
194
|
-
|
197
|
+
self._txtRules.append((rxRule, hlRule))
|
198
|
+
|
199
|
+
# Markdown Italic
|
200
|
+
rxRule = QRegularExpression(nwRegEx.FMT_EI)
|
201
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
202
|
+
hlRule = {
|
203
|
+
1: self._hStyles["markup"],
|
204
|
+
2: self._hStyles["italic"],
|
205
|
+
3: self._hStyles["markup"],
|
206
|
+
}
|
207
|
+
self._txtRules.append((rxRule, hlRule))
|
208
|
+
self._cmnRules.append((rxRule, hlRule))
|
209
|
+
|
210
|
+
# Markdown Bold
|
211
|
+
rxRule = QRegularExpression(nwRegEx.FMT_EB)
|
212
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
213
|
+
hlRule = {
|
214
|
+
1: self._hStyles["markup"],
|
215
|
+
2: self._hStyles["bold"],
|
216
|
+
3: self._hStyles["markup"],
|
217
|
+
}
|
218
|
+
self._txtRules.append((rxRule, hlRule))
|
219
|
+
self._cmnRules.append((rxRule, hlRule))
|
220
|
+
|
221
|
+
# Markdown Strikethrough
|
222
|
+
rxRule = QRegularExpression(nwRegEx.FMT_ST)
|
223
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
224
|
+
hlRule = {
|
225
|
+
1: self._hStyles["markup"],
|
226
|
+
2: self._hStyles["strike"],
|
227
|
+
3: self._hStyles["markup"],
|
228
|
+
}
|
229
|
+
self._txtRules.append((rxRule, hlRule))
|
230
|
+
self._cmnRules.append((rxRule, hlRule))
|
195
231
|
|
196
232
|
# Shortcodes
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
233
|
+
rxRule = QRegularExpression(nwRegEx.FMT_SC)
|
234
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
235
|
+
hlRule = {
|
236
|
+
1: self._hStyles["code"],
|
237
|
+
}
|
238
|
+
self._txtRules.append((rxRule, hlRule))
|
239
|
+
self._cmnRules.append((rxRule, hlRule))
|
202
240
|
|
203
241
|
# Shortcodes w/Value
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
242
|
+
rxRule = QRegularExpression(nwRegEx.FMT_SV)
|
243
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
244
|
+
hlRule = {
|
245
|
+
1: self._hStyles["code"],
|
246
|
+
2: self._hStyles["value"],
|
247
|
+
3: self._hStyles["code"],
|
248
|
+
}
|
249
|
+
self._txtRules.append((rxRule, hlRule))
|
250
|
+
self._cmnRules.append((rxRule, hlRule))
|
211
251
|
|
212
252
|
# Alignment Tags
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
253
|
+
rxRule = QRegularExpression(r"(^>{1,2}|<{1,2}$)")
|
254
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
255
|
+
hlRule = {
|
256
|
+
1: self._hStyles["markup"],
|
257
|
+
}
|
258
|
+
self._txtRules.append((rxRule, hlRule))
|
218
259
|
|
219
260
|
# Auto-Replace Tags
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
self._rxRules = []
|
228
|
-
for regEx, regRules in self._hRules:
|
229
|
-
hReg = QRegularExpression(regEx)
|
230
|
-
hReg.setPatternOptions(QRegularExpression.UseUnicodePropertiesOption)
|
231
|
-
self._rxRules.append((hReg, regRules))
|
261
|
+
rxRule = QRegularExpression(r"<(\S+?)>")
|
262
|
+
rxRule.setPatternOptions(QRegExUnicode)
|
263
|
+
hlRule = {
|
264
|
+
0: self._hStyles["replace"],
|
265
|
+
}
|
266
|
+
self._txtRules.append((rxRule, hlRule))
|
267
|
+
self._cmnRules.append((rxRule, hlRule))
|
232
268
|
|
233
269
|
return
|
234
270
|
|
@@ -282,6 +318,8 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
282
318
|
if self._tHandle is None or not text:
|
283
319
|
return
|
284
320
|
|
321
|
+
xOff = 0
|
322
|
+
hRules = None
|
285
323
|
if text.startswith("@"): # Keywords and commands
|
286
324
|
self.setCurrentBlockState(BLOCK_META)
|
287
325
|
index = SHARED.project.index
|
@@ -295,12 +333,12 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
295
333
|
self.setFormat(xPos, xLen, self._hStyles["keyword"])
|
296
334
|
elif isGood[n] and not self._isInactive:
|
297
335
|
one, two = index.parseValue(bit)
|
298
|
-
self.setFormat(xPos, len(one), self._hStyles["
|
336
|
+
self.setFormat(xPos, len(one), self._hStyles["tag"])
|
299
337
|
if two:
|
300
338
|
yPos = xPos + len(bit) - len(two)
|
301
339
|
self.setFormat(yPos, len(two), self._hStyles["optional"])
|
302
340
|
elif not self._isInactive:
|
303
|
-
self.setFormat(xPos, xLen, self._hStyles["
|
341
|
+
self.setFormat(xPos, xLen, self._hStyles["invalid"])
|
304
342
|
|
305
343
|
# We never want to run the spell checker on keyword/values,
|
306
344
|
# so we force a return here
|
@@ -339,43 +377,58 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
339
377
|
|
340
378
|
elif text.startswith("%"): # Comments
|
341
379
|
self.setCurrentBlockState(BLOCK_TEXT)
|
342
|
-
|
380
|
+
hRules = self._cmnRules
|
381
|
+
|
382
|
+
cStyle, cMod, _, cDot, cPos = processComment(text)
|
383
|
+
cLen = len(text) - cPos
|
384
|
+
xOff = cPos
|
343
385
|
if cStyle == nwComment.PLAIN:
|
344
|
-
self.setFormat(0,
|
386
|
+
self.setFormat(0, cLen, self._hStyles["hidden"])
|
387
|
+
elif cStyle == nwComment.IGNORE:
|
388
|
+
self.setFormat(0, cLen, self._hStyles["strike"])
|
389
|
+
return # No more processing for these
|
390
|
+
elif cMod:
|
391
|
+
self.setFormat(0, cDot, self._hStyles["modifier"])
|
392
|
+
self.setFormat(cDot, cPos - cDot, self._hStyles["value"])
|
393
|
+
self.setFormat(cPos, cLen, self._hStyles["note"])
|
345
394
|
else:
|
346
395
|
self.setFormat(0, cPos, self._hStyles["modifier"])
|
347
|
-
self.setFormat(cPos,
|
396
|
+
self.setFormat(cPos, cLen, self._hStyles["note"])
|
348
397
|
|
349
|
-
|
398
|
+
elif text.startswith("["): # Special Command
|
399
|
+
self.setCurrentBlockState(BLOCK_TEXT)
|
400
|
+
hRules = self._txtRules
|
401
|
+
|
402
|
+
sText = text.rstrip().lower()
|
403
|
+
if sText in ("[newpage]", "[new page]", "[vspace]"):
|
404
|
+
self.setFormat(0, len(text), self._hStyles["code"])
|
405
|
+
return
|
406
|
+
elif sText.startswith("[vspace:") and sText.endswith("]"):
|
407
|
+
tLen = len(sText)
|
408
|
+
tVal = checkInt(sText[8:-1], 0)
|
409
|
+
cVal = "value" if tVal > 0 else "invalid"
|
410
|
+
self.setFormat(0, 8, self._hStyles["code"])
|
411
|
+
self.setFormat(8, tLen-9, self._hStyles[cVal])
|
412
|
+
self.setFormat(tLen-1, tLen, self._hStyles["code"])
|
413
|
+
return
|
350
414
|
|
351
|
-
|
352
|
-
sText = text.rstrip().lower()
|
353
|
-
if sText in ("[newpage]", "[new page]", "[vspace]"):
|
354
|
-
self.setFormat(0, len(text), self._hStyles["code"])
|
355
|
-
return
|
356
|
-
elif sText.startswith("[vspace:") and sText.endswith("]"):
|
357
|
-
tLen = len(sText)
|
358
|
-
tVal = checkInt(sText[8:-1], 0)
|
359
|
-
cVal = "codevalue" if tVal > 0 else "codeinval"
|
360
|
-
self.setFormat(0, 8, self._hStyles["code"])
|
361
|
-
self.setFormat(8, tLen-9, self._hStyles[cVal])
|
362
|
-
self.setFormat(tLen-1, tLen, self._hStyles["code"])
|
363
|
-
return
|
364
|
-
|
365
|
-
# Regular Text
|
415
|
+
else: # Text Paragraph
|
366
416
|
self.setCurrentBlockState(BLOCK_TEXT)
|
367
|
-
|
368
|
-
|
417
|
+
hRules = self._txtRules
|
418
|
+
|
419
|
+
if hRules:
|
420
|
+
for rX, hRule in hRules:
|
421
|
+
rxItt = rX.globalMatch(text, xOff)
|
369
422
|
while rxItt.hasNext():
|
370
423
|
rxMatch = rxItt.next()
|
371
|
-
for xM in
|
424
|
+
for xM, hFmt in hRule.items():
|
372
425
|
xPos = rxMatch.capturedStart(xM)
|
373
|
-
|
374
|
-
for x in range(xPos,
|
375
|
-
|
376
|
-
if
|
377
|
-
|
378
|
-
self.setFormat(x, 1,
|
426
|
+
xEnd = rxMatch.capturedEnd(xM)
|
427
|
+
for x in range(xPos, xEnd):
|
428
|
+
cFmt = self.format(x)
|
429
|
+
if cFmt.fontStyleName() != "markup":
|
430
|
+
cFmt.merge(hFmt)
|
431
|
+
self.setFormat(x, 1, cFmt)
|
379
432
|
|
380
433
|
data = self.currentBlockUserData()
|
381
434
|
if not isinstance(data, TextBlockData):
|
@@ -383,11 +436,11 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
383
436
|
self.setCurrentBlockUserData(data)
|
384
437
|
|
385
438
|
if self._spellCheck:
|
386
|
-
for xPos, xLen in data.spellCheck(text):
|
439
|
+
for xPos, xLen in data.spellCheck(text, xOff):
|
387
440
|
for x in range(xPos, xPos+xLen):
|
388
|
-
|
389
|
-
|
390
|
-
self.setFormat(x, 1,
|
441
|
+
cFmt = self.format(x)
|
442
|
+
cFmt.merge(self._spellErr)
|
443
|
+
self.setFormat(x, 1, cFmt)
|
391
444
|
|
392
445
|
return
|
393
446
|
|
@@ -395,36 +448,37 @@ class GuiDocHighlighter(QSyntaxHighlighter):
|
|
395
448
|
# Internal Functions
|
396
449
|
##
|
397
450
|
|
398
|
-
def
|
399
|
-
|
400
|
-
|
401
|
-
|
402
|
-
"""
|
451
|
+
def _addCharFormat(
|
452
|
+
self, name: str, color: QColor | None = None,
|
453
|
+
style: str | None = None, size: float | None = None
|
454
|
+
) -> None:
|
455
|
+
"""Generate a highlighter character format."""
|
403
456
|
charFormat = QTextCharFormat()
|
457
|
+
charFormat.setFontStyleName(name)
|
404
458
|
|
405
|
-
if color
|
459
|
+
if color:
|
406
460
|
charFormat.setForeground(color)
|
407
461
|
|
408
|
-
if style
|
462
|
+
if style:
|
409
463
|
styles = style.split(",")
|
410
|
-
if "
|
464
|
+
if "b" in styles:
|
411
465
|
charFormat.setFontWeight(QFont.Weight.Bold)
|
412
|
-
if "
|
466
|
+
if "i" in styles:
|
413
467
|
charFormat.setFontItalic(True)
|
414
|
-
if "
|
468
|
+
if "s" in styles:
|
415
469
|
charFormat.setFontStrikeOut(True)
|
416
|
-
if "
|
470
|
+
if "err" in styles:
|
417
471
|
charFormat.setUnderlineColor(SHARED.theme.colError)
|
418
472
|
charFormat.setUnderlineStyle(QTextCharFormat.UnderlineStyle.SpellCheckUnderline)
|
419
|
-
if "
|
473
|
+
if "bg" in styles and color is not None:
|
420
474
|
charFormat.setBackground(QBrush(color, Qt.BrushStyle.SolidPattern))
|
421
475
|
|
422
|
-
if size
|
423
|
-
charFormat.setFontPointSize(
|
476
|
+
if size:
|
477
|
+
charFormat.setFontPointSize(round(size*CONFIG.textFont.pointSize()))
|
424
478
|
|
425
|
-
|
479
|
+
self._hStyles[name] = charFormat
|
426
480
|
|
427
|
-
|
481
|
+
return
|
428
482
|
|
429
483
|
|
430
484
|
class TextBlockData(QTextBlockUserData):
|
@@ -441,14 +495,14 @@ class TextBlockData(QTextBlockUserData):
|
|
441
495
|
"""Return spell error data from last check."""
|
442
496
|
return self._spellErrors
|
443
497
|
|
444
|
-
def spellCheck(self, text: str) -> list[tuple[int, int]]:
|
498
|
+
def spellCheck(self, text: str, offset: int) -> list[tuple[int, int]]:
|
445
499
|
"""Run the spell checker and cache the result, and return the
|
446
500
|
list of spell check errors.
|
447
501
|
"""
|
448
502
|
if "[" in text:
|
449
503
|
# Strip shortcodes
|
450
504
|
for rX in [SPELLSC, SPELLSV]:
|
451
|
-
rxItt = rX.globalMatch(text,
|
505
|
+
rxItt = rX.globalMatch(text, offset)
|
452
506
|
while rxItt.hasNext():
|
453
507
|
rxMatch = rxItt.next()
|
454
508
|
xPos = rxMatch.capturedStart(0)
|
@@ -457,12 +511,12 @@ class TextBlockData(QTextBlockUserData):
|
|
457
511
|
text = text[:xPos] + " "*xLen + text[xEnd:]
|
458
512
|
|
459
513
|
self._spellErrors = []
|
460
|
-
rxSpell = SPELLRX.globalMatch(text.replace("_", " "),
|
514
|
+
rxSpell = SPELLRX.globalMatch(text.replace("_", " "), offset)
|
461
515
|
while rxSpell.hasNext():
|
462
516
|
rxMatch = rxSpell.next()
|
463
517
|
if not SHARED.spelling.checkWord(rxMatch.captured(0)):
|
464
518
|
if not rxMatch.captured(0).isnumeric() and not rxMatch.captured(0).isupper():
|
465
|
-
self._spellErrors.append(
|
519
|
+
self._spellErrors.append(
|
520
|
+
(rxMatch.capturedStart(0), rxMatch.capturedLength(0))
|
521
|
+
)
|
466
522
|
return self._spellErrors
|
467
|
-
|
468
|
-
# END Class TextBlockData
|