eric6/QScintilla/MarkupProviders/RestructuredTextProvider.py

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

eric ide

mercurial