novelWriter 2.7.5__py3-none-any.whl → 2.8b1__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.
Files changed (196) hide show
  1. novelwriter/__init__.py +8 -7
  2. novelwriter/assets/icons/font_awesome.icons +22 -4
  3. novelwriter/assets/icons/material_filled_normal.icons +20 -2
  4. novelwriter/assets/icons/material_filled_thin.icons +20 -2
  5. novelwriter/assets/icons/material_rounded_normal.icons +20 -2
  6. novelwriter/assets/icons/material_rounded_thin.icons +20 -2
  7. novelwriter/assets/icons/material_sharp_normal.icons +20 -2
  8. novelwriter/assets/icons/material_sharp_thin.icons +20 -2
  9. novelwriter/assets/icons/remix_filled.icons +20 -2
  10. novelwriter/assets/icons/remix_outline.icons +20 -2
  11. novelwriter/assets/images/welcome.webp +0 -0
  12. novelwriter/assets/manual.pdf +0 -0
  13. novelwriter/assets/manual_fr.pdf +0 -0
  14. novelwriter/assets/sample.zip +0 -0
  15. novelwriter/assets/text/credits_en.htm +61 -11
  16. novelwriter/assets/themes/aura.conf +97 -0
  17. novelwriter/assets/themes/aura_bright.conf +95 -0
  18. novelwriter/assets/themes/aura_soft.conf +97 -0
  19. novelwriter/assets/themes/b2t_garden_dark.conf +97 -0
  20. novelwriter/assets/themes/b2t_garden_light.conf +97 -0
  21. novelwriter/assets/themes/b2t_suburb_dark.conf +97 -0
  22. novelwriter/assets/themes/b2t_suburb_light.conf +97 -0
  23. novelwriter/assets/themes/b4t_classic_o_dark.conf +97 -0
  24. novelwriter/assets/themes/b4t_classic_o_light.conf +97 -0
  25. novelwriter/assets/themes/b4t_modern_c_dark.conf +97 -0
  26. novelwriter/assets/themes/b4t_modern_c_light.conf +97 -0
  27. novelwriter/assets/themes/blue_streak_dark.conf +97 -0
  28. novelwriter/assets/themes/blue_streak_light.conf +97 -0
  29. novelwriter/assets/themes/castle_day.conf +95 -0
  30. novelwriter/assets/themes/castle_night.conf +95 -0
  31. novelwriter/assets/themes/catppuccin_latte.conf +97 -0
  32. novelwriter/assets/themes/catppuccin_mocha.conf +97 -0
  33. novelwriter/assets/themes/chalky_soil.conf +95 -0
  34. novelwriter/assets/themes/chernozem.conf +95 -0
  35. novelwriter/assets/themes/cyberpunk_night.conf +88 -40
  36. novelwriter/assets/themes/default_dark.conf +89 -41
  37. novelwriter/assets/themes/default_light.conf +89 -41
  38. novelwriter/assets/themes/dracula.conf +91 -42
  39. novelwriter/assets/themes/espresso.conf +97 -0
  40. novelwriter/assets/themes/everforest_dark.conf +97 -0
  41. novelwriter/assets/themes/everforest_light.conf +97 -0
  42. novelwriter/assets/themes/floral_daydream.conf +95 -0
  43. novelwriter/assets/themes/floral_midnight.conf +95 -0
  44. novelwriter/assets/themes/full_moon.conf +95 -0
  45. novelwriter/assets/themes/grey_dark.conf +97 -0
  46. novelwriter/assets/themes/grey_light.conf +97 -0
  47. novelwriter/assets/themes/horizon_dark.conf +97 -0
  48. novelwriter/assets/themes/horizon_light.conf +97 -0
  49. novelwriter/assets/themes/jewel_case_dark.conf +95 -0
  50. novelwriter/assets/themes/jewel_case_light.conf +95 -0
  51. novelwriter/assets/themes/lcars.conf +97 -0
  52. novelwriter/assets/themes/light_owl.conf +117 -0
  53. novelwriter/assets/themes/new_moon.conf +97 -0
  54. novelwriter/assets/themes/night_owl.conf +117 -0
  55. novelwriter/assets/themes/noctis.conf +129 -0
  56. novelwriter/assets/themes/noctis_lux.conf +129 -0
  57. novelwriter/assets/themes/nord.conf +97 -0
  58. novelwriter/assets/themes/nordlicht.conf +95 -0
  59. novelwriter/assets/themes/otium_dark.conf +95 -0
  60. novelwriter/assets/themes/otium_light.conf +95 -0
  61. novelwriter/assets/themes/paragon.conf +96 -0
  62. novelwriter/assets/themes/primer_light.conf +97 -0
  63. novelwriter/assets/themes/primer_night.conf +97 -0
  64. novelwriter/assets/themes/rose_pine.conf +97 -0
  65. novelwriter/assets/themes/rose_pine_dawn.conf +97 -0
  66. novelwriter/assets/themes/ruby_day.conf +95 -0
  67. novelwriter/assets/themes/ruby_night.conf +95 -0
  68. novelwriter/assets/themes/selenium_dark.conf +95 -0
  69. novelwriter/assets/themes/selenium_light.conf +95 -0
  70. novelwriter/assets/themes/sepia_dark.conf +95 -0
  71. novelwriter/assets/themes/sepia_light.conf +95 -0
  72. novelwriter/assets/themes/snazzy.conf +102 -40
  73. novelwriter/assets/themes/solarized_dark.conf +108 -40
  74. novelwriter/assets/themes/solarized_light.conf +108 -40
  75. novelwriter/assets/themes/sultana_light.conf +95 -0
  76. novelwriter/assets/themes/sultana_night.conf +95 -0
  77. novelwriter/assets/themes/tango_dark.conf +111 -0
  78. novelwriter/assets/themes/tango_light.conf +111 -0
  79. novelwriter/assets/themes/tomorrow.conf +117 -0
  80. novelwriter/assets/themes/tomorrow_night.conf +117 -0
  81. novelwriter/assets/themes/tomorrow_night_blue.conf +117 -0
  82. novelwriter/assets/themes/tomorrow_night_bright.conf +117 -0
  83. novelwriter/assets/themes/tomorrow_night_eighties.conf +117 -0
  84. novelwriter/assets/themes/vivid_black_green.conf +97 -0
  85. novelwriter/assets/themes/vivid_black_red.conf +97 -0
  86. novelwriter/assets/themes/vivid_white_green.conf +97 -0
  87. novelwriter/assets/themes/vivid_white_red.conf +97 -0
  88. novelwriter/assets/themes/warpgate.conf +96 -0
  89. novelwriter/assets/themes/waterlily_dark.conf +95 -0
  90. novelwriter/assets/themes/waterlily_light.conf +95 -0
  91. novelwriter/common.py +47 -17
  92. novelwriter/config.py +57 -62
  93. novelwriter/constants.py +32 -6
  94. novelwriter/core/buildsettings.py +3 -23
  95. novelwriter/core/coretools.py +21 -25
  96. novelwriter/core/docbuild.py +4 -9
  97. novelwriter/core/document.py +2 -6
  98. novelwriter/core/index.py +33 -53
  99. novelwriter/core/indexdata.py +17 -22
  100. novelwriter/core/item.py +11 -35
  101. novelwriter/core/itemmodel.py +5 -21
  102. novelwriter/core/novelmodel.py +3 -7
  103. novelwriter/core/options.py +3 -4
  104. novelwriter/core/project.py +31 -21
  105. novelwriter/core/projectdata.py +2 -21
  106. novelwriter/core/projectxml.py +13 -21
  107. novelwriter/core/sessions.py +2 -4
  108. novelwriter/core/spellcheck.py +12 -13
  109. novelwriter/core/status.py +27 -20
  110. novelwriter/core/storage.py +5 -10
  111. novelwriter/core/tree.py +6 -15
  112. novelwriter/dialogs/about.py +9 -10
  113. novelwriter/dialogs/docmerge.py +17 -14
  114. novelwriter/dialogs/docsplit.py +18 -14
  115. novelwriter/dialogs/editlabel.py +15 -9
  116. novelwriter/dialogs/preferences.py +69 -68
  117. novelwriter/dialogs/projectsettings.py +88 -67
  118. novelwriter/dialogs/quotes.py +15 -10
  119. novelwriter/dialogs/wordlist.py +18 -21
  120. novelwriter/enum.py +75 -30
  121. novelwriter/error.py +6 -11
  122. novelwriter/extensions/configlayout.py +8 -34
  123. novelwriter/extensions/eventfilters.py +3 -3
  124. novelwriter/extensions/modified.py +87 -32
  125. novelwriter/extensions/novelselector.py +13 -12
  126. novelwriter/extensions/pagedsidebar.py +10 -18
  127. novelwriter/extensions/progressbars.py +5 -11
  128. novelwriter/extensions/statusled.py +3 -6
  129. novelwriter/extensions/switch.py +8 -11
  130. novelwriter/extensions/switchbox.py +2 -11
  131. novelwriter/extensions/versioninfo.py +6 -7
  132. novelwriter/formats/shared.py +10 -2
  133. novelwriter/formats/todocx.py +15 -37
  134. novelwriter/formats/tohtml.py +52 -61
  135. novelwriter/formats/tokenizer.py +33 -64
  136. novelwriter/formats/tomarkdown.py +4 -11
  137. novelwriter/formats/toodt.py +12 -71
  138. novelwriter/formats/toqdoc.py +11 -21
  139. novelwriter/formats/toraw.py +2 -6
  140. novelwriter/gui/doceditor.py +160 -225
  141. novelwriter/gui/dochighlight.py +142 -101
  142. novelwriter/gui/docviewer.py +53 -84
  143. novelwriter/gui/docviewerpanel.py +18 -41
  144. novelwriter/gui/editordocument.py +12 -17
  145. novelwriter/gui/itemdetails.py +5 -14
  146. novelwriter/gui/mainmenu.py +24 -32
  147. novelwriter/gui/noveltree.py +13 -51
  148. novelwriter/gui/outline.py +20 -61
  149. novelwriter/gui/projtree.py +40 -96
  150. novelwriter/gui/search.py +9 -24
  151. novelwriter/gui/sidebar.py +54 -22
  152. novelwriter/gui/statusbar.py +7 -22
  153. novelwriter/gui/theme.py +482 -368
  154. novelwriter/guimain.py +87 -101
  155. novelwriter/shared.py +79 -48
  156. novelwriter/splash.py +9 -5
  157. novelwriter/text/comments.py +1 -1
  158. novelwriter/text/counting.py +9 -5
  159. novelwriter/text/patterns.py +20 -15
  160. novelwriter/tools/dictionaries.py +18 -16
  161. novelwriter/tools/lipsum.py +15 -17
  162. novelwriter/tools/manusbuild.py +25 -45
  163. novelwriter/tools/manuscript.py +94 -95
  164. novelwriter/tools/manussettings.py +149 -104
  165. novelwriter/tools/noveldetails.py +10 -24
  166. novelwriter/tools/welcome.py +24 -72
  167. novelwriter/tools/writingstats.py +17 -26
  168. novelwriter/types.py +23 -13
  169. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/METADATA +7 -7
  170. novelwriter-2.8b1.dist-info/RECORD +212 -0
  171. novelwriter/assets/images/welcome-dark.jpg +0 -0
  172. novelwriter/assets/images/welcome-light.jpg +0 -0
  173. novelwriter/assets/syntax/cyberpunk_night.conf +0 -28
  174. novelwriter/assets/syntax/default_dark.conf +0 -42
  175. novelwriter/assets/syntax/default_light.conf +0 -42
  176. novelwriter/assets/syntax/dracula.conf +0 -44
  177. novelwriter/assets/syntax/grey_dark.conf +0 -29
  178. novelwriter/assets/syntax/grey_light.conf +0 -29
  179. novelwriter/assets/syntax/light_owl.conf +0 -49
  180. novelwriter/assets/syntax/night_owl.conf +0 -49
  181. novelwriter/assets/syntax/snazzy.conf +0 -42
  182. novelwriter/assets/syntax/solarized_dark.conf +0 -29
  183. novelwriter/assets/syntax/solarized_light.conf +0 -29
  184. novelwriter/assets/syntax/tango.conf +0 -39
  185. novelwriter/assets/syntax/tomorrow.conf +0 -49
  186. novelwriter/assets/syntax/tomorrow_night.conf +0 -49
  187. novelwriter/assets/syntax/tomorrow_night_blue.conf +0 -49
  188. novelwriter/assets/syntax/tomorrow_night_bright.conf +0 -49
  189. novelwriter/assets/syntax/tomorrow_night_eighties.conf +0 -49
  190. novelwriter/assets/themes/default.conf +0 -3
  191. novelwriter-2.7.5.dist-info/RECORD +0 -163
  192. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/WHEEL +0 -0
  193. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/entry_points.txt +0 -0
  194. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/LICENSE.md +0 -0
  195. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/licenses/setup/LICENSE-Apache-2.0.txt +0 -0
  196. {novelwriter-2.7.5.dist-info → novelwriter-2.8b1.dist-info}/top_level.txt +0 -0
@@ -21,7 +21,7 @@ General Public License for more details.
21
21
 
22
22
  You should have received a copy of the GNU General Public License
23
23
  along with this program. If not, see <https://www.gnu.org/licenses/>.
24
- """
24
+ """ # noqa
25
25
  from __future__ import annotations
26
26
 
27
27
  import logging
@@ -55,6 +55,7 @@ logger = logging.getLogger(__name__)
55
55
 
56
56
 
57
57
  class ComStyle(NamedTuple):
58
+ """Comment style info."""
58
59
 
59
60
  label: str = ""
60
61
  labelClass: str = ""
@@ -71,16 +72,28 @@ COMMENT_STYLE = {
71
72
  nwComment.COMMENT: ComStyle(),
72
73
  nwComment.STORY: ComStyle("Story Structure", "modifier", "note"),
73
74
  }
74
- HEADINGS = [
75
+ COMMENT_TYPE = {
76
+ nwComment.PLAIN: BlockTyp.COMMENT,
77
+ nwComment.IGNORE: BlockTyp.COMMENT,
78
+ nwComment.SYNOPSIS: BlockTyp.SUMMARY,
79
+ nwComment.SHORT: BlockTyp.SUMMARY,
80
+ nwComment.NOTE: BlockTyp.NOTE,
81
+ nwComment.FOOTNOTE: BlockTyp.COMMENT,
82
+ nwComment.COMMENT: BlockTyp.COMMENT,
83
+ nwComment.STORY: BlockTyp.NOTE,
84
+ }
85
+ HEADING_BLOCKS = [
75
86
  BlockTyp.TITLE, BlockTyp.PART, BlockTyp.HEAD1,
76
87
  BlockTyp.HEAD2, BlockTyp.HEAD3, BlockTyp.HEAD4,
77
88
  ]
78
- SKIP_INDENT = [*HEADINGS, BlockTyp.SEP, BlockTyp.SKIP]
89
+ COMMENT_BLOCKS = (BlockTyp.COMMENT, BlockTyp.SUMMARY, BlockTyp.NOTE)
90
+ META_BLOCKS = (BlockTyp.COMMENT, BlockTyp.SUMMARY, BlockTyp.NOTE, BlockTyp.KEYWORD)
91
+ SKIP_INDENT = [*HEADING_BLOCKS, BlockTyp.SEP, BlockTyp.SKIP]
79
92
  B_EMPTY: T_Block = (BlockTyp.EMPTY, "", "", [], BlockFmt.NONE)
80
93
 
81
94
 
82
95
  class Tokenizer(ABC):
83
- """Core: Text Tokenizer Abstract Base Class
96
+ """Core: Text Tokenizer Abstract Base Class.
84
97
 
85
98
  This is the base class for all document build classes. It parses the
86
99
  novelWriter markup format and generates a registry of tokens and
@@ -189,6 +202,7 @@ class Tokenizer(ABC):
189
202
  (REGEX_PATTERNS.markdownItalic, [0, TextFmt.I_B, 0, TextFmt.I_E]),
190
203
  (REGEX_PATTERNS.markdownBold, [0, TextFmt.B_B, 0, TextFmt.B_E]),
191
204
  (REGEX_PATTERNS.markdownStrike, [0, TextFmt.D_B, 0, TextFmt.D_E]),
205
+ (REGEX_PATTERNS.markdownMark, [0, TextFmt.M_B, 0, TextFmt.M_E]),
192
206
  ]
193
207
 
194
208
  self._shortCodeFmt = {
@@ -211,8 +225,6 @@ class Tokenizer(ABC):
211
225
  self._dialogParser = DialogParser()
212
226
  self._dialogParser.initParser()
213
227
 
214
- return
215
-
216
228
  ##
217
229
  # Properties
218
230
  ##
@@ -240,94 +252,78 @@ class Tokenizer(ABC):
240
252
  """Set language for the document."""
241
253
  if language:
242
254
  self._dLocale = QLocale(language)
243
- return
244
255
 
245
256
  def setTheme(self, theme: TextDocumentTheme) -> None:
246
257
  """Set the document colour theme."""
247
258
  self._theme = theme
248
- return
249
259
 
250
260
  def setPartitionFormat(self, hFormat: str, hide: bool = False) -> None:
251
261
  """Set the partition format pattern."""
252
262
  self._fmtPart = hFormat.strip()
253
263
  self._hidePart = hide
254
- return
255
264
 
256
265
  def setChapterFormat(self, hFormat: str, hide: bool = False) -> None:
257
266
  """Set the chapter format pattern."""
258
267
  self._fmtChapter = hFormat.strip()
259
268
  self._hideChapter = hide
260
- return
261
269
 
262
270
  def setUnNumberedFormat(self, hFormat: str, hide: bool = False) -> None:
263
271
  """Set the unnumbered format pattern."""
264
272
  self._fmtUnNum = hFormat.strip()
265
273
  self._hideUnNum = hide
266
- return
267
274
 
268
275
  def setSceneFormat(self, hFormat: str, hide: bool = False) -> None:
269
276
  """Set the scene format pattern and hidden status."""
270
277
  self._fmtScene = hFormat.strip()
271
278
  self._hideScene = hide
272
- return
273
279
 
274
280
  def setHardSceneFormat(self, hFormat: str, hide: bool = False) -> None:
275
281
  """Set the hard scene format pattern and hidden status."""
276
282
  self._fmtHScene = hFormat.strip()
277
283
  self._hideHScene = hide
278
- return
279
284
 
280
285
  def setSectionFormat(self, hFormat: str, hide: bool = False) -> None:
281
286
  """Set the section format pattern and hidden status."""
282
287
  self._fmtSection = hFormat.strip()
283
288
  self._hideSection = hide
284
- return
285
289
 
286
290
  def setTitleStyle(self, center: bool, pageBreak: bool) -> None:
287
291
  """Set the title heading style."""
288
292
  self._titleStyle = BlockFmt.CENTRE if center else BlockFmt.NONE
289
293
  self._titleStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE
290
- return
291
294
 
292
295
  def setPartitionStyle(self, center: bool, pageBreak: bool) -> None:
293
296
  """Set the partition heading style."""
294
297
  self._partStyle = BlockFmt.CENTRE if center else BlockFmt.NONE
295
298
  self._partStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE
296
- return
297
299
 
298
300
  def setChapterStyle(self, center: bool, pageBreak: bool) -> None:
299
301
  """Set the chapter heading style."""
300
302
  self._chapterStyle = BlockFmt.CENTRE if center else BlockFmt.NONE
301
303
  self._chapterStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE
302
- return
303
304
 
304
305
  def setSceneStyle(self, center: bool, pageBreak: bool) -> None:
305
306
  """Set the scene heading style."""
306
307
  self._sceneStyle = BlockFmt.CENTRE if center else BlockFmt.NONE
307
308
  self._sceneStyle |= BlockFmt.PBB if pageBreak else BlockFmt.NONE
308
- return
309
309
 
310
310
  def setTextFont(self, font: QFont) -> None:
311
311
  """Set the build font."""
312
312
  self._textFont = fontMatcher(font)
313
- return
314
313
 
315
314
  def setLineHeight(self, height: float) -> None:
316
315
  """Set the line height between 0.5 and 5.0."""
317
316
  self._lineHeight = min(max(float(height), 0.5), 5.0)
318
- return
319
317
 
320
318
  def setHeadingStyles(self, color: bool, scale: bool, bold: bool) -> None:
321
319
  """Set text style for headings."""
322
320
  self._colorHeads = color
323
321
  self._scaleHeads = scale
324
322
  self._boldHeads = bold
325
- return
326
323
 
327
324
  def setBlockIndent(self, indent: float) -> None:
328
325
  """Set the block indent between 0.0 and 10.0."""
329
326
  self._blockIndent = min(max(float(indent), 0.0), 10.0)
330
- return
331
327
 
332
328
  def setFirstLineIndent(self, state: bool, indent: float, first: bool) -> None:
333
329
  """Set first line indent and whether to also indent first
@@ -336,67 +332,54 @@ class Tokenizer(ABC):
336
332
  self._firstIndent = state
337
333
  self._firstWidth = indent
338
334
  self._indentFirst = first
339
- return
340
335
 
341
336
  def setJustify(self, state: bool) -> None:
342
337
  """Enable or disable text justification."""
343
338
  self._doJustify = state
344
- return
345
339
 
346
340
  def setDialogHighlight(self, state: bool) -> None:
347
341
  """Enable or disable dialogue highlighting."""
348
342
  self._hlightDialog = state
349
- return
350
343
 
351
344
  def setTitleMargins(self, upper: float, lower: float) -> None:
352
345
  """Set the upper and lower title margin."""
353
346
  self._marginTitle = (float(upper), float(lower))
354
- return
355
347
 
356
348
  def setHead1Margins(self, upper: float, lower: float) -> None:
357
349
  """Set the upper and lower heading 1 margin."""
358
350
  self._marginHead1 = (float(upper), float(lower))
359
- return
360
351
 
361
352
  def setHead2Margins(self, upper: float, lower: float) -> None:
362
353
  """Set the upper and lower heading 2 margin."""
363
354
  self._marginHead2 = (float(upper), float(lower))
364
- return
365
355
 
366
356
  def setHead3Margins(self, upper: float, lower: float) -> None:
367
357
  """Set the upper and lower heading 3 margin."""
368
358
  self._marginHead3 = (float(upper), float(lower))
369
- return
370
359
 
371
360
  def setHead4Margins(self, upper: float, lower: float) -> None:
372
361
  """Set the upper and lower heading 4 margin."""
373
362
  self._marginHead4 = (float(upper), float(lower))
374
- return
375
363
 
376
364
  def setTextMargins(self, upper: float, lower: float) -> None:
377
365
  """Set the upper and lower text margin."""
378
366
  self._marginText = (float(upper), float(lower))
379
- return
380
367
 
381
368
  def setMetaMargins(self, upper: float, lower: float) -> None:
382
369
  """Set the upper and lower meta text margin."""
383
370
  self._marginMeta = (float(upper), float(lower))
384
- return
385
371
 
386
372
  def setSeparatorMargins(self, upper: float, lower: float) -> None:
387
373
  """Set the upper and lower meta text margin."""
388
374
  self._marginSep = (float(upper), float(lower))
389
- return
390
375
 
391
376
  def setLinkHeadings(self, state: bool) -> None:
392
377
  """Enable or disable adding an anchor before headings."""
393
378
  self._linkHeadings = state
394
- return
395
379
 
396
380
  def setBodyText(self, state: bool) -> None:
397
381
  """Include body text in build."""
398
382
  self._doBodyText = state
399
- return
400
383
 
401
384
  def setCommentType(self, comment: nwComment, state: bool) -> None:
402
385
  """Toggle the inclusion og certain comment types."""
@@ -404,22 +387,18 @@ class Tokenizer(ABC):
404
387
  self._doComments.add(comment)
405
388
  else:
406
389
  self._doComments.discard(comment)
407
- return
408
390
 
409
391
  def setKeywords(self, state: bool) -> None:
410
392
  """Include keywords in build."""
411
393
  self._doKeywords = state
412
- return
413
394
 
414
395
  def setIgnoredKeywords(self, keywords: str) -> None:
415
396
  """Comma separated string of keywords to ignore."""
416
397
  self._skipKeywords = set(x.lower().strip() for x in keywords.split(","))
417
- return
418
398
 
419
399
  def setKeepLineBreaks(self, state: bool) -> None:
420
400
  """Keep line breaks in paragraphs."""
421
401
  self._keepBreaks = state
422
- return
423
402
 
424
403
  ##
425
404
  # Class Methods
@@ -447,12 +426,10 @@ class Tokenizer(ABC):
447
426
  self._classes["tag"] = self._theme.tag
448
427
  self._classes["keyword"] = self._theme.keyword
449
428
  self._classes["optional"] = self._theme.optional
450
- return
451
429
 
452
430
  def setBreakNext(self) -> None:
453
431
  """Set a page break for next block."""
454
432
  self._breakNext = True
455
- return
456
433
 
457
434
  def addRootHeading(self, tHandle: str) -> None:
458
435
  """Add a heading at the start of a new root folder."""
@@ -478,8 +455,6 @@ class Tokenizer(ABC):
478
455
  if self._keepRaw:
479
456
  self._raw.append(f"#! {title}\n\n")
480
457
 
481
- return
482
-
483
458
  def setText(self, tHandle: str, text: str | None = None) -> None:
484
459
  """Set the text for the tokenizer from a handle. If text is not
485
460
  set, it's is loaded from the file.
@@ -490,7 +465,6 @@ class Tokenizer(ABC):
490
465
  self._text = text or self._project.storage.getDocumentText(tHandle)
491
466
  self._handle = tHandle
492
467
  self._isNovel = nwItem.itemLayout == nwItemLayout.DOCUMENT
493
- return
494
468
 
495
469
  def doPreProcessing(self) -> None:
496
470
  """Run pre-processing jobs before the text is tokenized."""
@@ -499,7 +473,6 @@ class Tokenizer(ABC):
499
473
  replace = {f"<{k}>": v for k, v in entry.items()}
500
474
  rxRep = re.compile("|".join([re.escape(k) for k in replace]), flags=re.DOTALL)
501
475
  self._text = rxRep.sub(lambda x: replace[x.group(0)], self._text)
502
- return
503
476
 
504
477
  def tokenizeText(self) -> None:
505
478
  """Scan the text for either lines starting with specific
@@ -577,13 +550,13 @@ class Tokenizer(ABC):
577
550
  self._breakNext = True
578
551
  continue
579
552
 
580
- elif sLine == "[vspace]":
553
+ if sLine == "[vspace]":
581
554
  tBlocks.append(
582
555
  (BlockTyp.SKIP, "", "", [], tStyle)
583
556
  )
584
557
  continue
585
558
 
586
- elif sLine.startswith("[vspace:") and sLine.endswith("]"):
559
+ if sLine.startswith("[vspace:") and sLine.endswith("]"):
587
560
  nSkip = checkInt(sLine[8:-1], 0)
588
561
  if nSkip >= 1:
589
562
  tBlocks.append(
@@ -618,7 +591,7 @@ class Tokenizer(ABC):
618
591
  bStyle = COMMENT_STYLE[cStyle]
619
592
  tLine, tFmt = self._formatComment(bStyle, cKey, cText)
620
593
  tBlocks.append((
621
- BlockTyp.COMMENT, "", tLine, tFmt, tStyle
594
+ COMMENT_TYPE[cStyle], "", tLine, tFmt, tStyle
622
595
  ))
623
596
 
624
597
  elif cStyle == nwComment.FOOTNOTE:
@@ -862,12 +835,12 @@ class Tokenizer(ABC):
862
835
  # We don't need to keep the empty lines after this pass
863
836
  pass
864
837
 
865
- elif cBlock[0] == BlockTyp.KEYWORD:
866
- # Adjust margins for lines in a list of keyword lines
838
+ elif cBlock[0] in (BlockTyp.KEYWORD, BlockTyp.NOTE):
839
+ # Adjust margins for lines in repeated meta blocks
867
840
  aStyle = cBlock[4]
868
- if pBlock[0] == BlockTyp.KEYWORD:
841
+ if pBlock[0] == cBlock[0]:
869
842
  aStyle |= BlockFmt.Z_TOP
870
- if nBlock[0] == BlockTyp.KEYWORD:
843
+ if nBlock[0] == cBlock[0]:
871
844
  aStyle |= BlockFmt.Z_BTM
872
845
  sBlocks.append((
873
846
  cBlock[0], cBlock[1], cBlock[2], cBlock[3], aStyle
@@ -949,8 +922,6 @@ class Tokenizer(ABC):
949
922
  text = tText.replace(nwHeadFmt.BR, " ").replace("&amp;", "&")
950
923
  self._outline[tKey] = f"{prefix}|{text}"
951
924
 
952
- return
953
-
954
925
  def countStats(self) -> None:
955
926
  """Count stats on the tokenized text."""
956
927
  titleCount = self._counts.get(nwStats.TITLES, 0)
@@ -991,7 +962,7 @@ class Tokenizer(ABC):
991
962
  allWordChars += nPWChars
992
963
  textWordChars += nPWChars
993
964
 
994
- elif tType in HEADINGS:
965
+ elif tType in HEADING_BLOCKS:
995
966
  titleCount += 1
996
967
  allWords += nWords
997
968
  titleWords += nWords
@@ -1005,7 +976,7 @@ class Tokenizer(ABC):
1005
976
  allChars += nChars
1006
977
  allWordChars += nWChars
1007
978
 
1008
- elif tType in (BlockTyp.COMMENT, BlockTyp.KEYWORD):
979
+ elif tType in META_BLOCKS:
1009
980
  words = tText.split()
1010
981
  allWords += len(words)
1011
982
  allChars += len(tText)
@@ -1026,8 +997,6 @@ class Tokenizer(ABC):
1026
997
  self._counts[nwStats.WCHARS_TEXT] = textWordChars
1027
998
  self._counts[nwStats.WCHARS_TITLE] = titleWordChars
1028
999
 
1029
- return
1030
-
1031
1000
  ##
1032
1001
  # Internal Functions
1033
1002
  ##
@@ -1169,6 +1138,12 @@ class Tokenizer(ABC):
1169
1138
 
1170
1139
 
1171
1140
  class HeadingFormatter:
1141
+ """Core: Format Text Headings.
1142
+
1143
+ This class holds the various chapter and scene counters and can
1144
+ apply the Build Settings header format settings based on internal
1145
+ counter state.
1146
+ """
1172
1147
 
1173
1148
  def __init__(
1174
1149
  self,
@@ -1182,35 +1157,29 @@ class HeadingFormatter:
1182
1157
  self._chapter = chapter
1183
1158
  self._scene = scene
1184
1159
  self._absolute = absolute
1185
- return
1186
1160
 
1187
1161
  def setHandle(self, tHandle: str | None) -> None:
1188
1162
  """Set the handle currently being processed."""
1189
1163
  self._handle = tHandle
1190
- return
1191
1164
 
1192
1165
  def incChapter(self) -> None:
1193
1166
  """Increment the chapter counter."""
1194
1167
  self._chapter += 1
1195
- return
1196
1168
 
1197
1169
  def incScene(self) -> None:
1198
1170
  """Increment the scene counters."""
1199
1171
  self._scene += 1
1200
1172
  self._absolute += 1
1201
- return
1202
1173
 
1203
1174
  def resetAll(self) -> None:
1204
1175
  """Reset all counters."""
1205
1176
  self._chapter = 0
1206
1177
  self._scene = 0
1207
1178
  self._absolute = 0
1208
- return
1209
1179
 
1210
1180
  def resetScene(self) -> None:
1211
1181
  """Reset the chapter scene counter."""
1212
1182
  self._scene = 0
1213
- return
1214
1183
 
1215
1184
  def apply(self, hFormat: str, text: str, nHead: int) -> str:
1216
1185
  """Apply formatting to a specific heading."""
@@ -20,7 +20,7 @@ General Public License for more details.
20
20
 
21
21
  You should have received a copy of the GNU General Public License
22
22
  along with this program. If not, see <https://www.gnu.org/licenses/>.
23
- """
23
+ """ # noqa
24
24
  from __future__ import annotations
25
25
 
26
26
  import logging
@@ -29,7 +29,7 @@ from typing import TYPE_CHECKING
29
29
 
30
30
  from novelwriter.constants import nwUnicode
31
31
  from novelwriter.formats.shared import BlockFmt, BlockTyp, T_Formats, TextFmt
32
- from novelwriter.formats.tokenizer import Tokenizer
32
+ from novelwriter.formats.tokenizer import COMMENT_BLOCKS, Tokenizer
33
33
 
34
34
  if TYPE_CHECKING:
35
35
  from pathlib import Path
@@ -79,7 +79,7 @@ EXT_MD = {
79
79
 
80
80
 
81
81
  class ToMarkdown(Tokenizer):
82
- """Core: Markdown Document Writer
82
+ """Core: Markdown Document Writer.
83
83
 
84
84
  Extend the Tokenizer class to writer Markdown output. It supports
85
85
  both Standard Markdown and Extended Markdown. The class also
@@ -91,7 +91,6 @@ class ToMarkdown(Tokenizer):
91
91
  self._extended = extended
92
92
  self._usedNotes: dict[str, int] = {}
93
93
  self._usedFields: list[tuple[int, str]] = []
94
- return
95
94
 
96
95
  ##
97
96
  # Class Methods
@@ -144,7 +143,7 @@ class ToMarkdown(Tokenizer):
144
143
  elif tType == BlockTyp.SKIP:
145
144
  lines.append(f"{cSkip}\n\n")
146
145
 
147
- elif tType == BlockTyp.COMMENT:
146
+ elif tType in COMMENT_BLOCKS:
148
147
  lines.append(f"{self._formatText(tText, tFormat, mTags)}\n\n")
149
148
 
150
149
  elif tType == BlockTyp.KEYWORD:
@@ -153,8 +152,6 @@ class ToMarkdown(Tokenizer):
153
152
 
154
153
  self._pages.append("".join(lines))
155
154
 
156
- return
157
-
158
155
  def closeDocument(self) -> None:
159
156
  """Run close document tasks."""
160
157
  # Replace fields if there are stats available
@@ -181,20 +178,16 @@ class ToMarkdown(Tokenizer):
181
178
  lines.append("\n")
182
179
  self._pages.append("".join(lines))
183
180
 
184
- return
185
-
186
181
  def saveDocument(self, path: Path) -> None:
187
182
  """Save the data to a plain text file."""
188
183
  with open(path, mode="w", encoding="utf-8") as outFile:
189
184
  outFile.write("".join(self._pages))
190
185
  logger.info("Wrote file: %s", path)
191
- return
192
186
 
193
187
  def replaceTabs(self, nSpaces: int = 8, spaceChar: str = " ") -> None:
194
188
  """Replace tabs with spaces."""
195
189
  spaces = spaceChar*nSpaces
196
190
  self._pages = [p.replace("\t", spaces) for p in self._pages]
197
- return
198
191
 
199
192
  ##
200
193
  # Internal Functions