novelWriter 2.4.3__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.
Files changed (109) hide show
  1. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/METADATA +4 -5
  2. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/RECORD +109 -101
  3. novelwriter/__init__.py +33 -39
  4. novelwriter/assets/i18n/project_en_GB.json +1 -0
  5. novelwriter/assets/icons/typicons_dark/icons.conf +2 -0
  6. novelwriter/assets/icons/typicons_dark/nw_font.svg +4 -0
  7. novelwriter/assets/icons/typicons_dark/nw_quote.svg +4 -0
  8. novelwriter/assets/icons/typicons_light/icons.conf +2 -0
  9. novelwriter/assets/icons/typicons_light/nw_font.svg +4 -0
  10. novelwriter/assets/icons/typicons_light/nw_quote.svg +4 -0
  11. novelwriter/assets/manual.pdf +0 -0
  12. novelwriter/assets/sample.zip +0 -0
  13. novelwriter/assets/syntax/cyberpunk_night.conf +5 -3
  14. novelwriter/assets/syntax/default_dark.conf +32 -18
  15. novelwriter/assets/syntax/default_light.conf +24 -10
  16. novelwriter/assets/syntax/dracula.conf +44 -0
  17. novelwriter/assets/syntax/grey_dark.conf +5 -4
  18. novelwriter/assets/syntax/grey_light.conf +5 -4
  19. novelwriter/assets/syntax/light_owl.conf +7 -6
  20. novelwriter/assets/syntax/night_owl.conf +7 -6
  21. novelwriter/assets/syntax/snazzy.conf +42 -0
  22. novelwriter/assets/syntax/solarized_dark.conf +4 -3
  23. novelwriter/assets/syntax/solarized_light.conf +4 -3
  24. novelwriter/assets/syntax/tango.conf +27 -11
  25. novelwriter/assets/syntax/tomorrow.conf +6 -5
  26. novelwriter/assets/syntax/tomorrow_night.conf +7 -6
  27. novelwriter/assets/syntax/tomorrow_night_blue.conf +6 -5
  28. novelwriter/assets/syntax/tomorrow_night_bright.conf +6 -5
  29. novelwriter/assets/syntax/tomorrow_night_eighties.conf +6 -5
  30. novelwriter/assets/text/credits_en.htm +4 -1
  31. novelwriter/assets/themes/cyberpunk_night.conf +2 -0
  32. novelwriter/assets/themes/default_dark.conf +1 -0
  33. novelwriter/assets/themes/default_light.conf +1 -0
  34. novelwriter/assets/themes/dracula.conf +47 -0
  35. novelwriter/assets/themes/solarized_dark.conf +1 -0
  36. novelwriter/assets/themes/solarized_light.conf +1 -0
  37. novelwriter/common.py +31 -9
  38. novelwriter/config.py +118 -84
  39. novelwriter/constants.py +40 -26
  40. novelwriter/core/buildsettings.py +63 -66
  41. novelwriter/core/coretools.py +2 -22
  42. novelwriter/core/docbuild.py +51 -40
  43. novelwriter/core/document.py +3 -5
  44. novelwriter/core/index.py +115 -45
  45. novelwriter/core/item.py +8 -19
  46. novelwriter/core/options.py +2 -4
  47. novelwriter/core/project.py +23 -57
  48. novelwriter/core/projectdata.py +1 -3
  49. novelwriter/core/projectxml.py +12 -15
  50. novelwriter/core/sessions.py +3 -5
  51. novelwriter/core/spellcheck.py +4 -9
  52. novelwriter/core/status.py +211 -164
  53. novelwriter/core/storage.py +0 -8
  54. novelwriter/core/tohtml.py +94 -100
  55. novelwriter/core/tokenizer.py +199 -112
  56. novelwriter/core/{tomd.py → tomarkdown.py} +97 -78
  57. novelwriter/core/toodt.py +212 -148
  58. novelwriter/core/toqdoc.py +403 -0
  59. novelwriter/core/tree.py +5 -7
  60. novelwriter/dialogs/about.py +3 -5
  61. novelwriter/dialogs/docmerge.py +1 -3
  62. novelwriter/dialogs/docsplit.py +1 -3
  63. novelwriter/dialogs/editlabel.py +0 -2
  64. novelwriter/dialogs/preferences.py +111 -88
  65. novelwriter/dialogs/projectsettings.py +216 -180
  66. novelwriter/dialogs/quotes.py +3 -4
  67. novelwriter/dialogs/wordlist.py +3 -9
  68. novelwriter/enum.py +31 -25
  69. novelwriter/error.py +8 -15
  70. novelwriter/extensions/circularprogress.py +5 -6
  71. novelwriter/extensions/configlayout.py +18 -18
  72. novelwriter/extensions/eventfilters.py +1 -5
  73. novelwriter/extensions/modified.py +50 -13
  74. novelwriter/extensions/novelselector.py +1 -3
  75. novelwriter/extensions/pagedsidebar.py +9 -12
  76. novelwriter/extensions/simpleprogress.py +1 -3
  77. novelwriter/extensions/statusled.py +1 -3
  78. novelwriter/extensions/switch.py +4 -6
  79. novelwriter/extensions/switchbox.py +7 -6
  80. novelwriter/extensions/versioninfo.py +3 -9
  81. novelwriter/gui/doceditor.py +98 -126
  82. novelwriter/gui/dochighlight.py +237 -183
  83. novelwriter/gui/docviewer.py +46 -94
  84. novelwriter/gui/docviewerpanel.py +3 -10
  85. novelwriter/gui/editordocument.py +1 -3
  86. novelwriter/gui/itemdetails.py +7 -11
  87. novelwriter/gui/mainmenu.py +11 -7
  88. novelwriter/gui/noveltree.py +11 -24
  89. novelwriter/gui/outline.py +11 -23
  90. novelwriter/gui/projtree.py +26 -43
  91. novelwriter/gui/search.py +1 -3
  92. novelwriter/gui/sidebar.py +2 -6
  93. novelwriter/gui/statusbar.py +6 -10
  94. novelwriter/gui/theme.py +23 -48
  95. novelwriter/guimain.py +50 -71
  96. novelwriter/shared.py +30 -15
  97. novelwriter/tools/dictionaries.py +8 -12
  98. novelwriter/tools/lipsum.py +2 -4
  99. novelwriter/tools/manusbuild.py +1 -3
  100. novelwriter/tools/manuscript.py +66 -145
  101. novelwriter/tools/manussettings.py +67 -73
  102. novelwriter/tools/noveldetails.py +6 -11
  103. novelwriter/tools/welcome.py +2 -16
  104. novelwriter/tools/writingstats.py +6 -9
  105. novelwriter/types.py +45 -3
  106. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/LICENSE.md +0 -0
  107. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/WHEEL +0 -0
  108. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/entry_points.txt +0 -0
  109. {novelWriter-2.4.3.dist-info → novelWriter-2.5b1.dist-info}/top_level.txt +0 -0
@@ -29,11 +29,50 @@ from pathlib import Path
29
29
 
30
30
  from novelwriter.constants import nwHeadFmt, nwLabels, nwUnicode
31
31
  from novelwriter.core.project import NWProject
32
- from novelwriter.core.tokenizer import Tokenizer
32
+ from novelwriter.core.tokenizer import T_Formats, Tokenizer
33
33
 
34
34
  logger = logging.getLogger(__name__)
35
35
 
36
36
 
37
+ # Standard Markdown
38
+ STD_MD = {
39
+ Tokenizer.FMT_B_B: "**",
40
+ Tokenizer.FMT_B_E: "**",
41
+ Tokenizer.FMT_I_B: "_",
42
+ Tokenizer.FMT_I_E: "_",
43
+ Tokenizer.FMT_D_B: "",
44
+ Tokenizer.FMT_D_E: "",
45
+ Tokenizer.FMT_U_B: "",
46
+ Tokenizer.FMT_U_E: "",
47
+ Tokenizer.FMT_M_B: "",
48
+ Tokenizer.FMT_M_E: "",
49
+ Tokenizer.FMT_SUP_B: "",
50
+ Tokenizer.FMT_SUP_E: "",
51
+ Tokenizer.FMT_SUB_B: "",
52
+ Tokenizer.FMT_SUB_E: "",
53
+ Tokenizer.FMT_STRIP: "",
54
+ }
55
+
56
+ # Extended Markdown
57
+ EXT_MD = {
58
+ Tokenizer.FMT_B_B: "**",
59
+ Tokenizer.FMT_B_E: "**",
60
+ Tokenizer.FMT_I_B: "_",
61
+ Tokenizer.FMT_I_E: "_",
62
+ Tokenizer.FMT_D_B: "~~",
63
+ Tokenizer.FMT_D_E: "~~",
64
+ Tokenizer.FMT_U_B: "",
65
+ Tokenizer.FMT_U_E: "",
66
+ Tokenizer.FMT_M_B: "==",
67
+ Tokenizer.FMT_M_E: "==",
68
+ Tokenizer.FMT_SUP_B: "^",
69
+ Tokenizer.FMT_SUP_E: "^",
70
+ Tokenizer.FMT_SUB_B: "~",
71
+ Tokenizer.FMT_SUB_E: "~",
72
+ Tokenizer.FMT_STRIP: "",
73
+ }
74
+
75
+
37
76
  class ToMarkdown(Tokenizer):
38
77
  """Core: Markdown Document Writer
39
78
 
@@ -42,14 +81,11 @@ class ToMarkdown(Tokenizer):
42
81
  supports concatenating novelWriter markup files.
43
82
  """
44
83
 
45
- M_STD = 0 # Standard Markdown
46
- M_EXT = 1 # Extended Markdown
47
-
48
84
  def __init__(self, project: NWProject) -> None:
49
85
  super().__init__(project)
50
- self._genMode = self.M_STD
51
86
  self._fullMD: list[str] = []
52
- self._preserveBreaks = True
87
+ self._usedNotes: dict[str, int] = {}
88
+ self._extended = True
53
89
  return
54
90
 
55
91
  ##
@@ -65,19 +101,9 @@ class ToMarkdown(Tokenizer):
65
101
  # Setters
66
102
  ##
67
103
 
68
- def setStandardMarkdown(self) -> None:
69
- """Set the converter to use standard Markdown formatting."""
70
- self._genMode = self.M_STD
71
- return
72
-
73
- def setExtendedMarkdown(self) -> None:
104
+ def setExtendedMarkdown(self, state: bool) -> None:
74
105
  """Set the converter to use Extended Markdown formatting."""
75
- self._genMode = self.M_EXT
76
- return
77
-
78
- def setPreserveBreaks(self, state: bool) -> None:
79
- """Preserve line breaks in paragraphs."""
80
- self._preserveBreaks = state
106
+ self._extended = state
81
107
  return
82
108
 
83
109
  ##
@@ -90,58 +116,21 @@ class ToMarkdown(Tokenizer):
90
116
 
91
117
  def doConvert(self) -> None:
92
118
  """Convert the list of text tokens into a Markdown document."""
93
- if self._genMode == self.M_STD:
94
- # Standard Markdown
95
- mdTags = {
96
- self.FMT_B_B: "**",
97
- self.FMT_B_E: "**",
98
- self.FMT_I_B: "_",
99
- self.FMT_I_E: "_",
100
- self.FMT_D_B: "",
101
- self.FMT_D_E: "",
102
- self.FMT_U_B: "",
103
- self.FMT_U_E: "",
104
- self.FMT_M_B: "",
105
- self.FMT_M_E: "",
106
- self.FMT_SUP_B: "",
107
- self.FMT_SUP_E: "",
108
- self.FMT_SUB_B: "",
109
- self.FMT_SUB_E: "",
110
- }
111
- cSkip = ""
112
- else:
113
- # Extended Markdown
114
- mdTags = {
115
- self.FMT_B_B: "**",
116
- self.FMT_B_E: "**",
117
- self.FMT_I_B: "_",
118
- self.FMT_I_E: "_",
119
- self.FMT_D_B: "~~",
120
- self.FMT_D_E: "~~",
121
- self.FMT_U_B: "",
122
- self.FMT_U_E: "",
123
- self.FMT_M_B: "==",
124
- self.FMT_M_E: "==",
125
- self.FMT_SUP_B: "^",
126
- self.FMT_SUP_E: "^",
127
- self.FMT_SUB_B: "~",
128
- self.FMT_SUB_E: "~",
129
- }
130
- cSkip = nwUnicode.U_MMSP
131
-
132
119
  self._result = ""
133
120
 
134
- para = []
135
- lines = []
136
- lineSep = " \n" if self._preserveBreaks else " "
121
+ if self._extended:
122
+ mTags = EXT_MD
123
+ cSkip = nwUnicode.U_MMSP
124
+ else:
125
+ mTags = STD_MD
126
+ cSkip = ""
137
127
 
128
+ lines = []
138
129
  for tType, _, tText, tFormat, tStyle in self._tokens:
139
130
 
140
- if tType == self.T_EMPTY:
141
- if para:
142
- tTemp = (lineSep.join(para)).rstrip(" ")
143
- lines.append(f"{tTemp}\n\n")
144
- para = []
131
+ if tType == self.T_TEXT:
132
+ tTemp = self._formatText(tText, tFormat, mTags).replace("\n", " \n")
133
+ lines.append(f"{tTemp}\n\n")
145
134
 
146
135
  elif tType == self.T_TITLE:
147
136
  tHead = tText.replace(nwHeadFmt.BR, "\n")
@@ -169,23 +158,17 @@ class ToMarkdown(Tokenizer):
169
158
  elif tType == self.T_SKIP:
170
159
  lines.append(f"{cSkip}\n\n")
171
160
 
172
- elif tType == self.T_TEXT:
173
- tTemp = tText
174
- for pos, fmt in reversed(tFormat):
175
- tTemp = f"{tTemp[:pos]}{mdTags[fmt]}{tTemp[pos:]}"
176
- para.append(tTemp.rstrip())
177
-
178
161
  elif tType == self.T_SYNOPSIS and self._doSynopsis:
179
162
  label = self._localLookup("Synopsis")
180
- lines.append(f"**{label}:** {tText}\n\n")
163
+ lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
181
164
 
182
165
  elif tType == self.T_SHORT and self._doSynopsis:
183
166
  label = self._localLookup("Short Description")
184
- lines.append(f"**{label}:** {tText}\n\n")
167
+ lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
185
168
 
186
169
  elif tType == self.T_COMMENT and self._doComments:
187
170
  label = self._localLookup("Comment")
188
- lines.append(f"**{label}:** {tText}\n\n")
171
+ lines.append(f"**{label}:** {self._formatText(tText, tFormat, mTags)}\n\n")
189
172
 
190
173
  elif tType == self.T_KEYWORD and self._doKeywords:
191
174
  lines.append(self._formatKeywords(tText, tStyle))
@@ -195,6 +178,27 @@ class ToMarkdown(Tokenizer):
195
178
 
196
179
  return
197
180
 
181
+ def appendFootnotes(self) -> None:
182
+ """Append the footnotes in the buffer."""
183
+ if self._usedNotes:
184
+ tags = EXT_MD if self._extended else STD_MD
185
+ footnotes = self._localLookup("Footnotes")
186
+
187
+ lines = []
188
+ lines.append(f"### {footnotes}\n\n")
189
+ for key, index in self._usedNotes.items():
190
+ if content := self._footnotes.get(key):
191
+ marker = f"{index}. "
192
+ text = self._formatText(*content, tags)
193
+ lines.append(f"{marker}{text}\n")
194
+ lines.append("\n")
195
+
196
+ result = "".join(lines)
197
+ self._result += result
198
+ self._fullMD.append(result)
199
+
200
+ return
201
+
198
202
  def saveMarkdown(self, path: str | Path) -> None:
199
203
  """Save the data to a plain text file."""
200
204
  with open(path, mode="w", encoding="utf-8") as outFile:
@@ -206,14 +210,31 @@ class ToMarkdown(Tokenizer):
206
210
  """Replace tabs with spaces."""
207
211
  spaces = spaceChar*nSpaces
208
212
  self._fullMD = [p.replace("\t", spaces) for p in self._fullMD]
209
- if self._keepMarkdown:
210
- self._allMarkdown = [p.replace("\t", spaces) for p in self._allMarkdown]
213
+ if self._keepMD:
214
+ self._markdown = [p.replace("\t", spaces) for p in self._markdown]
211
215
  return
212
216
 
213
217
  ##
214
218
  # Internal Functions
215
219
  ##
216
220
 
221
+ def _formatText(self, text: str, tFmt: T_Formats, tags: dict[int, str]) -> str:
222
+ """Apply formatting tags to text."""
223
+ temp = text
224
+ for pos, fmt, data in reversed(tFmt):
225
+ md = ""
226
+ if fmt == self.FMT_FNOTE:
227
+ if data in self._footnotes:
228
+ index = len(self._usedNotes) + 1
229
+ self._usedNotes[data] = index
230
+ md = f"[{index}]"
231
+ else:
232
+ md = "[ERR]"
233
+ else:
234
+ md = tags.get(fmt, "")
235
+ temp = f"{temp[:pos]}{md}{temp[pos:]}"
236
+ return temp
237
+
217
238
  def _formatKeywords(self, text: str, style: int) -> str:
218
239
  """Apply Markdown formatting to keywords."""
219
240
  valid, bits, _ = self._project.index.scanThis("@"+text)
@@ -229,5 +250,3 @@ class ToMarkdown(Tokenizer):
229
250
  result += " \n" if style & self.A_Z_BTMMRG else "\n\n"
230
251
 
231
252
  return result
232
-
233
- # END Class ToMarkdown