src/eric7/QScintilla/MarkupProviders/RestructuredTextProvider.py

branch
eric7
changeset 9209
b99e7fd55fd3
parent 8881
54e42bc2437a
child 9221
bf71ee032bb4
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/eric7/QScintilla/MarkupProviders/RestructuredTextProvider.py	Thu Jul 07 11:23:56 2022 +0200
@@ -0,0 +1,431 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2017 - 2022 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing the reStructured Text markup provider.
+"""
+
+from PyQt6.QtCore import QCoreApplication
+from PyQt6.QtWidgets import QDialog, QInputDialog
+
+from .MarkupBase import MarkupBase
+
+
+class RestructuredTextProvider(MarkupBase):
+    """
+    Class implementing the reStructured Text markup provider.
+    """
+    def __init__(self):
+        """
+        Constructor
+        """
+        super().__init__()
+        
+        self.__headerChars = ["=", "-", "~", "+", "#", "^"]
+    
+    def kind(self):
+        """
+        Public method to get the markup kind.
+        
+        @return string with markup kind
+        @rtype str
+        """
+        return "rest"
+    
+    def hasBold(self):
+        """
+        Public method to indicate the availability of bold markup.
+        
+        @return flag indicating the availability of bold markup
+        @rtype bool
+        """
+        return True
+    
+    def bold(self, editor):
+        """
+        Public method to generate bold text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__insertMarkup("**", editor)
+    
+    def hasItalic(self):
+        """
+        Public method to indicate the availability of italic markup.
+        
+        @return flag indicating the availability of italic markup
+        @rtype bool
+        """
+        return True
+    
+    def italic(self, editor):
+        """
+        Public method to generate italic text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__insertMarkup("*", editor)
+    
+    def headerLevels(self):
+        """
+        Public method to determine the available header levels.
+        
+        @return supported header levels
+        @rtype int
+        """
+        return len(self.__headerChars)
+    
+    def header(self, editor, level):
+        """
+        Public method to generate a header.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        @param level header level
+        @type int
+        """
+        if editor is None or level > self.headerLevels():
+            return
+        
+        editor.beginUndoAction()
+        cline, cindex = editor.getCursorPosition()
+        if editor.hasSelection() and cindex == 0:
+            cline -= 1
+        lineSeparator = editor.getLineSeparator()
+        if not editor.text(cline).endswith(lineSeparator):
+            editor.insertAt(lineSeparator, cline, len(editor.text(cline)))
+        lineLength = len(editor.text(cline)) - len(lineSeparator)
+        editor.insertAt(
+            lineLength * self.__headerChars[level - 1] + lineSeparator,
+            cline + 1, 0)
+        editor.setCursorPosition(cline + 2, 0)
+        editor.endUndoAction()
+    
+    def hasCode(self):
+        """
+        Public method to indicate the availability of inline code markup.
+        
+        @return flag indicating the availability of inline code markup
+        @rtype bool
+        """
+        return True
+    
+    def code(self, editor):
+        """
+        Public method to generate inline code text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__insertMarkup("``", editor)
+    
+    def hasCodeBlock(self):
+        """
+        Public method to indicate the availability of code block markup.
+        
+        @return flag indicating the availability of code block markup
+        @rtype bool
+        """
+        return True
+    
+    def codeBlock(self, editor):
+        """
+        Public method to generate code block text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            sline, sindex, eline, eindex = editor.getSelection()
+            if not editor.text(sline).startswith((" ", "\t")):
+                # assume that all selected lines need indentation,
+                # if first line needs it
+                endLine = eline if eindex > 0 else eline - 1
+                for line in range(sline, endLine + 1):
+                    editor.insertAt("    ", line, 0)
+            editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
+        else:
+            editor.insert("::{0}{0}    ".format(lineSeparator))
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + 2, 4)
+        editor.endUndoAction()
+    
+    def __insertMarkup(self, markup, editor):
+        """
+        Private method to insert the specified markup.
+        
+        If the editor has selected text, this text is enclosed by the given
+        markup. If no text is selected, the markup is inserted at the cursor
+        position and the cursor is positioned in between.
+        
+        @param markup markup string to be inserted
+        @type str
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            newText = "{0}{1}{0}".format(markup, editor.selectedText())
+            editor.replaceSelectedText(newText)
+        else:
+            editor.insert(2 * markup)
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline, cindex + len(markup))
+        editor.endUndoAction()
+    
+    def hasHyperlink(self):
+        """
+        Public method to indicate the availability of hyperlink markup.
+        
+        @return flag indicating the availability of hyperlink markup
+        @rtype bool
+        """
+        return True
+    
+    def hyperlink(self, editor):
+        """
+        Public method to generate hyperlink text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        from .HyperlinkMarkupDialog import HyperlinkMarkupDialog
+        dlg = HyperlinkMarkupDialog(False, True, noTitle=True)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            text, target, _ = dlg.getData()
+            
+            link1 = "`{0}`_".format(text)
+            link2 = ".. _`{0}`:".format(text)
+            if target:
+                link2 = "{0} {1}".format(link2, target)
+            
+            lineSeparator = editor.getLineSeparator()
+            editor.beginUndoAction()
+            cline, cindex = editor.getCursorPosition()
+            editor.insert(link1)
+            
+            line = cline
+            while line < editor.lines():
+                if editor.text(line).strip() == "":
+                    # found end of block
+                    break
+                line += 1
+            if line == editor.lines():
+                # reached end of document
+                editor.insertAt(2 * lineSeparator, line, 0)
+                editor.insertAt(link2, line + 2, 0)
+            else:
+                # find end of link block or start of next block
+                line += 1
+                while line < editor.lines():
+                    if not editor.text(line).startswith(".. _"):
+                        break
+                    line += 1
+                if editor.text(line).strip():
+                    sep = 2 * lineSeparator
+                else:
+                    sep = lineSeparator
+                editor.insertAt("{0}{1}".format(link2, sep), line, 0)
+            
+            editor.setCursorPosition(cline, cindex + len(link1))
+            editor.endUndoAction()
+    
+    def hasLine(self):
+        """
+        Public method to indicate the availability of a horizontal line markup.
+        
+        @return flag indicating the availability of a horizontal line markup
+        @rtype bool
+        """
+        return True
+    
+    def line(self, editor):
+        """
+        Public method to generate a horizontal line text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        markup = "{0}-----{0}{0}".format(lineSeparator)
+        editor.insert(markup)
+        cline, cindex = editor.getCursorPosition()
+        editor.setCursorPosition(cline + 3, 0)
+        editor.endUndoAction()
+    
+    def hasQuote(self):
+        """
+        Public method to indicate the availability of block quote markup.
+        
+        @return flag indicating the availability of block quote markup
+        @rtype bool
+        """
+        return True
+    
+    def quote(self, editor):
+        """
+        Public method to generate block quote text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        markup = "> "
+        sline, sindex, eline, eindex = editor.getSelection()
+        for line in range(sline, eline + 1 if eindex > 0 else eline):
+            editor.insertAt(markup, line, 0)
+        editor.insertAt("::{0}{0}".format(lineSeparator), sline, 0)
+        editor.setCursorPosition(eline + 2, eindex)
+        editor.endUndoAction()
+    
+    def hasImage(self):
+        """
+        Public method to indicate the availability of image markup.
+        
+        @return flag indicating the availability of image markup
+        @rtype bool
+        """
+        return True
+    
+    def image(self, editor):
+        """
+        Public method to generate image text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        if editor is None:
+            return
+        
+        from .ImageMarkupDialog import ImageMarkupDialog
+        dlg = ImageMarkupDialog(ImageMarkupDialog.RestMode)
+        if dlg.exec() == QDialog.DialogCode.Accepted:
+            address, altText, title, originalSize, width, height = (
+                dlg.getData()
+            )
+            
+            lineSeparator = editor.getLineSeparator()
+            markup = ".. image:: {0}{1}".format(address, lineSeparator)
+            lines = 1
+            if altText:
+                markup += "   :alt: {0}{1}".format(altText, lineSeparator)
+                lines += 1
+            if not originalSize:
+                markup += "   :height: {0}px{1}".format(height, lineSeparator)
+                markup += "   :width: {0}px{1}".format(width, lineSeparator)
+                lines += 2
+            
+            editor.beginUndoAction()
+            editor.insert(markup)
+            cline, cindex = editor.getCursorPosition()
+            editor.setCursorPosition(cline + lines, 0)
+            editor.endUndoAction()
+    
+    def hasBulletedList(self):
+        """
+        Public method to indicate the availability of bulleted list markup.
+        
+        @return flag indicating the availability of bulleted list markup
+        @rtype bool
+        """
+        return True
+    
+    def bulletedList(self, editor):
+        """
+        Public method to generate bulleted list text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__makeList(editor, False)
+    
+    def hasNumberedList(self):
+        """
+        Public method to indicate the availability of numbered list markup.
+        
+        @return flag indicating the availability of numbered list markup
+        @rtype bool
+        """
+        return True
+    
+    def numberedList(self, editor):
+        """
+        Public method to generate numbered list text.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        """
+        self.__makeList(editor, True)
+    
+    def __makeList(self, editor, numberedList):
+        """
+        Private method to generate the desired list markup.
+        
+        @param editor reference to the editor to work on
+        @type Editor
+        @param numberedList flag indicating the generation of a numbered list
+        @type bool
+        """
+        if editor is None:
+            return
+        
+        markup = "  #. " if numberedList else "  * "
+        lineSeparator = editor.getLineSeparator()
+        editor.beginUndoAction()
+        if editor.hasSelectedText():
+            startLine, startIndex, endLine, endIndex = (
+                editor.getSelection()
+            )
+            if endIndex == 0:
+                endLine -= 1
+            for line in range(startLine, endLine + 1):
+                editor.insertAt(markup, line, 0)
+            editor.setCursorPosition(endLine + 1, 0)
+        else:
+            listElements, ok = QInputDialog.getInt(
+                None,
+                QCoreApplication.translate(
+                    "RestructuredTextProvider", "Create List"),
+                QCoreApplication.translate(
+                    "RestructuredTextProvider",
+                    "Enter desired number of list elements:"),
+                0, 0, 99, 1)
+            if ok:
+                if listElements == 0:
+                    listElements = 1
+                cline, cindex = editor.getCursorPosition()
+                listBody = (
+                    listElements * "{1}{0}".format(lineSeparator, markup)
+                )
+                if cindex == 0:
+                    editor.insertAt(listBody, cline, cindex)
+                    editor.setCursorPosition(cline, len(markup))
+                else:
+                    if cline == editor.lines() - 1:
+                        editor.insertAt(lineSeparator, cline, 1000)
+                    editor.insertAt(listBody, cline + 1, 0)
+                    editor.setCursorPosition(cline + 1, len(markup))
+        editor.endUndoAction()

eric ide

mercurial