src/eric7/QScintilla/MarkupProviders/RestructuredTextProvider.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
equal deleted inserted replaced
9208:3fc8dfeb6ebe 9209:b99e7fd55fd3
1 # -*- coding: utf-8 -*-
2
3 # Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
4 #
5
6 """
7 Module implementing the reStructured Text markup provider.
8 """
9
10 from PyQt6.QtCore import QCoreApplication
11 from PyQt6.QtWidgets import QDialog, QInputDialog
12
13 from .MarkupBase import MarkupBase
14
15
16 class RestructuredTextProvider(MarkupBase):
17 """
18 Class implementing the reStructured Text markup provider.
19 """
20 def __init__(self):
21 """
22 Constructor
23 """
24 super().__init__()
25
26 self.__headerChars = ["=", "-", "~", "+", "#", "^"]
27
28 def kind(self):
29 """
30 Public method to get the markup kind.
31
32 @return string with markup kind
33 @rtype str
34 """
35 return "rest"
36
37 def hasBold(self):
38 """
39 Public method to indicate the availability of bold markup.
40
41 @return flag indicating the availability of bold markup
42 @rtype bool
43 """
44 return True
45
46 def bold(self, editor):
47 """
48 Public method to generate bold text.
49
50 @param editor reference to the editor to work on
51 @type Editor
52 """
53 self.__insertMarkup("**", editor)
54
55 def hasItalic(self):
56 """
57 Public method to indicate the availability of italic markup.
58
59 @return flag indicating the availability of italic markup
60 @rtype bool
61 """
62 return True
63
64 def italic(self, editor):
65 """
66 Public method to generate italic text.
67
68 @param editor reference to the editor to work on
69 @type Editor
70 """
71 self.__insertMarkup("*", editor)
72
73 def headerLevels(self):
74 """
75 Public method to determine the available header levels.
76
77 @return supported header levels
78 @rtype int
79 """
80 return len(self.__headerChars)
81
82 def header(self, editor, level):
83 """
84 Public method to generate a header.
85
86 @param editor reference to the editor to work on
87 @type Editor
88 @param level header level
89 @type int
90 """
91 if editor is None or level > self.headerLevels():
92 return
93
94 editor.beginUndoAction()
95 cline, cindex = editor.getCursorPosition()
96 if editor.hasSelection() and cindex == 0:
97 cline -= 1
98 lineSeparator = editor.getLineSeparator()
99 if not editor.text(cline).endswith(lineSeparator):
100 editor.insertAt(lineSeparator, cline, len(editor.text(cline)))
101 lineLength = len(editor.text(cline)) - len(lineSeparator)
102 editor.insertAt(
103 lineLength * self.__headerChars[level - 1] + lineSeparator,
104 cline + 1, 0)
105 editor.setCursorPosition(cline + 2, 0)
106 editor.endUndoAction()
107
108 def hasCode(self):
109 """
110 Public method to indicate the availability of inline code markup.
111
112 @return flag indicating the availability of inline code markup
113 @rtype bool
114 """
115 return True
116
117 def code(self, editor):
118 """
119 Public method to generate inline code text.
120
121 @param editor reference to the editor to work on
122 @type Editor
123 """
124 self.__insertMarkup("``", editor)
125
126 def hasCodeBlock(self):
127 """
128 Public method to indicate the availability of code block markup.
129
130 @return flag indicating the availability of code block markup
131 @rtype bool
132 """
133 return True
134
135 def codeBlock(self, editor):
136 """
137 Public method to generate code block text.
138
139 @param editor reference to the editor to work on
140 @type Editor
141 """
142 if editor is None:
143 return
144
145 lineSeparator = editor.getLineSeparator()
146 editor.beginUndoAction()
147 if editor.hasSelectedText():
148 sline, sindex, eline, eindex = editor.getSelection()
149 if not editor.text(sline).startswith((" ", "\t")):
150 # assume that all selected lines need indentation,
151 # if first line needs it
152 endLine = eline if eindex > 0 else eline - 1
153 for line in range(sline, endLine + 1):
154 editor.insertAt(" ", line, 0)
155 editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
156 else:
157 editor.insert("::{0}{0} ".format(lineSeparator))
158 cline, cindex = editor.getCursorPosition()
159 editor.setCursorPosition(cline + 2, 4)
160 editor.endUndoAction()
161
162 def __insertMarkup(self, markup, editor):
163 """
164 Private method to insert the specified markup.
165
166 If the editor has selected text, this text is enclosed by the given
167 markup. If no text is selected, the markup is inserted at the cursor
168 position and the cursor is positioned in between.
169
170 @param markup markup string to be inserted
171 @type str
172 @param editor reference to the editor to work on
173 @type Editor
174 """
175 if editor is None:
176 return
177
178 editor.beginUndoAction()
179 if editor.hasSelectedText():
180 newText = "{0}{1}{0}".format(markup, editor.selectedText())
181 editor.replaceSelectedText(newText)
182 else:
183 editor.insert(2 * markup)
184 cline, cindex = editor.getCursorPosition()
185 editor.setCursorPosition(cline, cindex + len(markup))
186 editor.endUndoAction()
187
188 def hasHyperlink(self):
189 """
190 Public method to indicate the availability of hyperlink markup.
191
192 @return flag indicating the availability of hyperlink markup
193 @rtype bool
194 """
195 return True
196
197 def hyperlink(self, editor):
198 """
199 Public method to generate hyperlink text.
200
201 @param editor reference to the editor to work on
202 @type Editor
203 """
204 if editor is None:
205 return
206
207 from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
208 dlg = HyperlinkMarkupDialog(False, True, noTitle=True)
209 if dlg.exec() == QDialog.DialogCode.Accepted:
210 text, target, _ = dlg.getData()
211
212 link1 = "`{0}`_".format(text)
213 link2 = ".. _`{0}`:".format(text)
214 if target:
215 link2 = "{0} {1}".format(link2, target)
216
217 lineSeparator = editor.getLineSeparator()
218 editor.beginUndoAction()
219 cline, cindex = editor.getCursorPosition()
220 editor.insert(link1)
221
222 line = cline
223 while line < editor.lines():
224 if editor.text(line).strip() == "":
225 # found end of block
226 break
227 line += 1
228 if line == editor.lines():
229 # reached end of document
230 editor.insertAt(2 * lineSeparator, line, 0)
231 editor.insertAt(link2, line + 2, 0)
232 else:
233 # find end of link block or start of next block
234 line += 1
235 while line < editor.lines():
236 if not editor.text(line).startswith(".. _"):
237 break
238 line += 1
239 if editor.text(line).strip():
240 sep = 2 * lineSeparator
241 else:
242 sep = lineSeparator
243 editor.insertAt("{0}{1}".format(link2, sep), line, 0)
244
245 editor.setCursorPosition(cline, cindex + len(link1))
246 editor.endUndoAction()
247
248 def hasLine(self):
249 """
250 Public method to indicate the availability of a horizontal line markup.
251
252 @return flag indicating the availability of a horizontal line markup
253 @rtype bool
254 """
255 return True
256
257 def line(self, editor):
258 """
259 Public method to generate a horizontal line text.
260
261 @param editor reference to the editor to work on
262 @type Editor
263 """
264 if editor is None:
265 return
266
267 lineSeparator = editor.getLineSeparator()
268 editor.beginUndoAction()
269 markup = "{0}-----{0}{0}".format(lineSeparator)
270 editor.insert(markup)
271 cline, cindex = editor.getCursorPosition()
272 editor.setCursorPosition(cline + 3, 0)
273 editor.endUndoAction()
274
275 def hasQuote(self):
276 """
277 Public method to indicate the availability of block quote markup.
278
279 @return flag indicating the availability of block quote markup
280 @rtype bool
281 """
282 return True
283
284 def quote(self, editor):
285 """
286 Public method to generate block quote text.
287
288 @param editor reference to the editor to work on
289 @type Editor
290 """
291 if editor is None:
292 return
293
294 lineSeparator = editor.getLineSeparator()
295 editor.beginUndoAction()
296 markup = "> "
297 sline, sindex, eline, eindex = editor.getSelection()
298 for line in range(sline, eline + 1 if eindex > 0 else eline):
299 editor.insertAt(markup, line, 0)
300 editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
301 editor.setCursorPosition(eline + 2, eindex)
302 editor.endUndoAction()
303
304 def hasImage(self):
305 """
306 Public method to indicate the availability of image markup.
307
308 @return flag indicating the availability of image markup
309 @rtype bool
310 """
311 return True
312
313 def image(self, editor):
314 """
315 Public method to generate image text.
316
317 @param editor reference to the editor to work on
318 @type Editor
319 """
320 if editor is None:
321 return
322
323 from .ImageMarkupDialog import ImageMarkupDialog
324 dlg = ImageMarkupDialog(ImageMarkupDialog.RestMode)
325 if dlg.exec() == QDialog.DialogCode.Accepted:
326 address, altText, title, originalSize, width, height = (
327 dlg.getData()
328 )
329
330 lineSeparator = editor.getLineSeparator()
331 markup = ".. image:: {0}{1}".format(address, lineSeparator)
332 lines = 1
333 if altText:
334 markup += " :alt: {0}{1}".format(altText, lineSeparator)
335 lines += 1
336 if not originalSize:
337 markup += " :height: {0}px{1}".format(height, lineSeparator)
338 markup += " :width: {0}px{1}".format(width, lineSeparator)
339 lines += 2
340
341 editor.beginUndoAction()
342 editor.insert(markup)
343 cline, cindex = editor.getCursorPosition()
344 editor.setCursorPosition(cline + lines, 0)
345 editor.endUndoAction()
346
347 def hasBulletedList(self):
348 """
349 Public method to indicate the availability of bulleted list markup.
350
351 @return flag indicating the availability of bulleted list markup
352 @rtype bool
353 """
354 return True
355
356 def bulletedList(self, editor):
357 """
358 Public method to generate bulleted list text.
359
360 @param editor reference to the editor to work on
361 @type Editor
362 """
363 self.__makeList(editor, False)
364
365 def hasNumberedList(self):
366 """
367 Public method to indicate the availability of numbered list markup.
368
369 @return flag indicating the availability of numbered list markup
370 @rtype bool
371 """
372 return True
373
374 def numberedList(self, editor):
375 """
376 Public method to generate numbered list text.
377
378 @param editor reference to the editor to work on
379 @type Editor
380 """
381 self.__makeList(editor, True)
382
383 def __makeList(self, editor, numberedList):
384 """
385 Private method to generate the desired list markup.
386
387 @param editor reference to the editor to work on
388 @type Editor
389 @param numberedList flag indicating the generation of a numbered list
390 @type bool
391 """
392 if editor is None:
393 return
394
395 markup = " #. " if numberedList else " * "
396 lineSeparator = editor.getLineSeparator()
397 editor.beginUndoAction()
398 if editor.hasSelectedText():
399 startLine, startIndex, endLine, endIndex = (
400 editor.getSelection()
401 )
402 if endIndex == 0:
403 endLine -= 1
404 for line in range(startLine, endLine + 1):
405 editor.insertAt(markup, line, 0)
406 editor.setCursorPosition(endLine + 1, 0)
407 else:
408 listElements, ok = QInputDialog.getInt(
409 None,
410 QCoreApplication.translate(
411 "RestructuredTextProvider", "Create List"),
412 QCoreApplication.translate(
413 "RestructuredTextProvider",
414 "Enter desired number of list elements:"),
415 0, 0, 99, 1)
416 if ok:
417 if listElements == 0:
418 listElements = 1
419 cline, cindex = editor.getCursorPosition()
420 listBody = (
421 listElements * "{1}{0}".format(lineSeparator, markup)
422 )
423 if cindex == 0:
424 editor.insertAt(listBody, cline, cindex)
425 editor.setCursorPosition(cline, len(markup))
426 else:
427 if cline == editor.lines() - 1:
428 editor.insertAt(lineSeparator, cline, 1000)
429 editor.insertAt(listBody, cline + 1, 0)
430 editor.setCursorPosition(cline + 1, len(markup))
431 editor.endUndoAction()

eric ide

mercurial