src/eric7/ThirdParty/Jasy/jasy/script/api/Comment.py

branch
eric7
changeset 9955
aa02420279fe
parent 9954
7c5fa3eef082
child 9956
5b138f996a1e
diff -r 7c5fa3eef082 -r aa02420279fe src/eric7/ThirdParty/Jasy/jasy/script/api/Comment.py
--- a/src/eric7/ThirdParty/Jasy/jasy/script/api/Comment.py	Fri Mar 31 13:39:51 2023 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,677 +0,0 @@
-#
-# Jasy - Web Tooling Framework
-# Copyright 2010-2012 Zynga Inc.
-# Copyright 2013-2014 Sebastian Werner
-#
-
-from __future__ import unicode_literals
-
-import re
-
-import jasy.core.Text as Text
-import jasy.core.Console as Console
-
-from jasy import UserError
-from jasy.script.util import *
-
-
-# Used to measure the doc indent size (with leading stars in front of content)
-docIndentReg = re.compile(r"^(\s*\*\s*)(\S*)")
-
-# Used to split type lists as supported by throw, return and params
-listSplit = re.compile("\s*\|\s*")
-
-# Used to remove markup sequences after doc processing of comment text
-stripMarkup = re.compile(r"<.*?>")
-
-
-
-# Matches return blocks in comments
-returnMatcher = re.compile(r"^\s*\{([a-zA-Z0-9_ \.\|\[\]]+)\}")
-
-# Matches type definitions in comments
-typeMatcher = re.compile(r"^\s*\{=([a-zA-Z0-9_ \.]+)\}")
-
-# Matches tags
-tagMatcher = re.compile(r"#([a-zA-Z][a-zA-Z0-9]+)(\((\S+)\))?(\s|$)")
-
-# Matches param declarations in own dialect
-paramMatcher = re.compile(r"@([a-zA-Z0-9_][a-zA-Z0-9_\.]*[a-zA-Z0-9_]|[a-zA-Z0-9_]+)(\s*\{([a-zA-Z0-9_ \.\|\[\]]+?)(\s*\.{3}\s*)?((\s*\?\s*(\S+))|(\s*\?\s*))?\})?")
-
-# Matches links in own dialect
-linkMatcher = re.compile(r"(\{((static|member|property|event)\:)?([a-zA-Z0-9_\.]+)?(\#([a-zA-Z0-9_]+))?\})")
-
-# matches backticks and has a built-in failsafe for backticks which do not terminate on the same line
-tickMatcher = re.compile(r"(`[^\n`]*?`)")
-
-
-class CommentException(Exception):
-    """
-    Thrown when errors during comment processing are detected.
-    """
-
-    def __init__(self, message, lineNo=0):
-        Exception.__init__(self, "Comment error: %s (line: %s)" % (message, lineNo+1))
-
-
-
-
-class Comment():
-    """
-    Comment class is attached to parsed nodes and used to store all comment related information.
-
-    The class supports a new Markdown and TomDoc inspired dialect to make developers life easier and work less repeative.
-    """
-
-    # Relation to code
-    context = None
-
-    # Dictionary of tags
-    tags = None
-
-    # Dictionary of params
-    params = None
-
-    # List of return types
-    returns = None
-
-    # Static type
-    type = None
-
-    # Collected text of the comment (without the extracted doc relevant data)
-    text = None
-
-    # Text with extracted / parsed data
-    __processedText = None
-
-    # Text of the comment converted to HTML including highlighting (only for doc comment)
-    __highlightedText = None
-
-    # Text / Code Blocks in the comment
-    __blocks = None
-
-
-    def __init__(self, text, context=None, lineNo=0, indent="", fileId=None):
-
-        # Store context (relation to code)
-        self.context = context
-
-        # Store fileId
-        self.fileId = fileId
-
-        # Figure out the type of the comment based on the starting characters
-
-        # Inline comments
-        if text.startswith("//"):
-            # "// hello" => "   hello"
-            text = "  " + text[2:]
-            self.variant = "single"
-
-        # Doc comments
-        elif text.startswith("/**"):
-            # "/** hello */" => "    hello "
-            text = "   " + text[3:-2]
-            self.variant = "doc"
-
-        # Protected comments which should not be removed (e.g these are used for license blocks)
-        elif text.startswith("/*!"):
-            # "/*! hello */" => "    hello "
-            text = "   " + text[3:-2]
-            self.variant = "protected"
-
-        # A normal multiline comment
-        elif text.startswith("/*"):
-            # "/* hello */" => "   hello "
-            text = "  " + text[2:-2]
-            self.variant = "multi"
-
-        else:
-            raise CommentException("Invalid comment text: %s" % text, lineNo)
-
-        # Multi line comments need to have their indentation removed
-        if "\n" in text:
-            text = self.__outdent(text, indent, lineNo)
-
-        # For single line comments strip the surrounding whitespace
-        else:
-            # " hello " => "hello"
-            text = text.strip()
-
-        # The text of the comment before any processing took place
-        self.text = text
-
-
-        # Perform annotation parsing, markdown conversion and code highlighting on doc blocks
-        if self.variant == "doc":
-
-            # Separate text and code blocks
-            self.__blocks = self.__splitBlocks(text)
-
-            # Re-combine everything and apply processing and formatting
-            plainText = '' # text without annotations but with markdown
-            for b in self.__blocks:
-
-                if b["type"] == "comment":
-
-                    processed = self.__processDoc(b["text"], lineNo)
-                    b["processed"] = processed
-
-                    if "<" in processed:
-                        plainText += stripMarkup.sub("", processed)
-
-                    else:
-                        plainText += processed
-
-                else:
-                    plainText += "\n\n" + b["text"] + "\n\n"
-
-            # The without any annotations
-            self.text = plainText.strip()
-
-
-    def __splitBlocks(self, text):
-        """
-        Splits up text and code blocks in comments.
-
-        This will try to use hoedown for Markdown parsing if available and will
-        fallback to a simpler implementation in order to allow processing of
-        doc parameters and links without hoedown being installed.
-        """
-
-        if not Text.supportsMarkdown:
-            return self.__splitSimple(text)
-
-        marked = Text.markdownToHtml(text)
-
-        def unescape(html):
-            html = html.replace('&lt;', '<')
-            html = html.replace('&gt;', '>')
-            html = html.replace('&amp;', '&')
-            html = html.replace('&quot;', '"')
-            return html.replace('&#39;', "'")
-
-        parts = []
-
-        lineNo = 0
-        lines = text.split("\n")
-        markedLines = marked.split("\n")
-
-        i = 0
-        while i < len(markedLines):
-
-            l = markedLines[i]
-
-            # the original text of the line
-            parsed = unescape(stripMarkup.sub("", l))
-
-            # start of a code block, grab all text before it and move it into a block
-            if l.startswith('<pre><code>'):
-
-                # everything since the last code block and before this one must be text
-                comment = []
-                for s in range(lineNo, len(lines)):
-
-                    source = lines[s]
-                    if source.strip() == parsed.strip():
-                        lineNo = s
-                        break
-
-                    comment.append(source)
-
-                parts.append({
-                    "type": "comment",
-                    "text": "\n".join(comment)
-                })
-
-                # Find the end of the code block
-                e = i
-                while i < len(markedLines):
-                    l = markedLines[i]
-                    i += 1
-
-                    if l.startswith('</code></pre>'):
-                        break
-
-                lineCount = (i - e) - 1
-
-                # add the code block
-                parts.append({
-                    "type": "code",
-                    "text": "\n".join(lines[lineNo:lineNo + lineCount])
-                })
-
-                lineNo += lineCount
-
-            else:
-                i += 1
-
-        # append the rest of the comment as text
-        parts.append({
-            "type": "comment",
-            "text": "\n".join(lines[lineNo:])
-        })
-
-        return parts
-
-
-    def __splitSimple(self, text):
-        """Splits comment text and code blocks by manually parsing a subset of markdown"""
-
-        inCode = False
-        oldIndent = 0
-        parts = []
-        wasEmpty = False
-        wasList = False
-
-        lineNo = 0
-        lines = text.split("\n")
-
-        for s, l in enumerate(lines):
-
-            # ignore empty lines
-            if not l.strip() == "":
-
-                # get indentation value and change
-                indent = len(l) - len(l.lstrip())
-                change = indent - oldIndent
-
-                # detect code blocks
-                if change >= 4 and wasEmpty:
-                    if not wasList:
-                        oldIndent = indent
-                        inCode = True
-
-                        parts.append({
-                            "type": "comment",
-                            "text": "\n".join(lines[lineNo:s])
-                        })
-
-                        lineNo = s
-
-                # detect outdents
-                elif change < 0:
-                    inCode = False
-
-                    parts.append({
-                        "type": "code",
-                        "text": "\n".join(lines[lineNo:s - 1])
-                    })
-
-                    lineNo = s
-
-                # only keep track of old previous indentation outside of comments
-                if not inCode:
-                    oldIndent = indent
-
-                # remember whether this marked a list or not
-                wasList = l.strip().startswith('-') or l.strip().startswith('*')
-                wasEmpty = False
-
-            else:
-                wasEmpty = True
-
-        parts.append({
-            "type": "code" if inCode else "comment",
-            "text": "\n".join(lines[lineNo:])
-        })
-
-        return parts
-
-
-    def getHtml(self, highlight=True):
-        """
-        Returns the comment text converted to HTML
-
-        :param highlight: Whether to highlight the code
-        :type highlight: bool
-        """
-
-        if not Text.supportsMarkdown:
-            raise UserError("Markdown is not supported by the system. Documentation comments could converted to HTML.")
-
-        if highlight:
-
-            if self.__highlightedText is None:
-
-                highlightedText = ""
-
-                for block in self.__blocks:
-
-                    if block["type"] == "comment":
-                        highlightedText += Text.highlightCodeBlocks(Text.markdownToHtml(block["processed"]))
-                    else:
-                        highlightedText += "\n%s" % Text.highlightCodeBlocks(Text.markdownToHtml(block["text"]))
-
-                self.__highlightedText = highlightedText
-
-            return self.__highlightedText
-
-        else:
-
-            if self.__processedText is None:
-
-                processedText = ""
-
-                for block in self.__blocks:
-
-                    if block["type"] == "comment":
-                        processedText += Text.markdownToHtml(block["processed"])
-                    else:
-                        processedText += "\n%s\n\n" % block["text"]
-
-                self.__processedText = processedText.strip()
-
-            return self.__processedText
-
-
-    def hasContent(self):
-        return self.variant == "doc" and len(self.text)
-
-
-    def getTags(self):
-        return self.tags
-
-
-    def hasTag(self, name):
-        if not self.tags:
-            return False
-
-        return name in self.tags
-
-
-    def __outdent(self, text, indent, startLineNo):
-        """
-        Outdent multi line comment text and filtering empty lines
-        """
-
-        lines = []
-
-        # First, split up the comments lines and remove the leading indentation
-        for lineNo, line in enumerate((indent+text).split("\n")):
-
-            if line.startswith(indent):
-                lines.append(line[len(indent):].rstrip())
-
-            elif line.strip() == "":
-                lines.append("")
-
-            else:
-                # Only warn for doc comments, otherwise it might just be code commented out
-                # which is sometimes formatted pretty crazy when commented out
-                if self.variant == "doc":
-                    Console.warn("Could not outdent doc comment at line %s in %s", startLineNo+lineNo, self.fileId)
-
-                return text
-
-        # Find first line with real content, then grab the one after it to get the
-        # characters which need
-        outdentString = ""
-        for lineNo, line in enumerate(lines):
-
-            if line != "" and line.strip() != "":
-                matchedDocIndent = docIndentReg.match(line)
-
-                if not matchedDocIndent:
-                    # As soon as we find a non doc indent like line we stop
-                    break
-
-                elif matchedDocIndent.group(2) != "":
-                    # otherwise we look for content behind the indent to get the
-                    # correct real indent (with spaces)
-                    outdentString = matchedDocIndent.group(1)
-                    break
-
-            lineNo += 1
-
-        # Process outdenting to all lines (remove the outdentString from the start of the lines)
-        if outdentString != "":
-
-            lineNo = 0
-            outdentStringLen = len(outdentString)
-
-            for lineNo, line in enumerate(lines):
-                if len(line) <= outdentStringLen:
-                    lines[lineNo] = ""
-
-                else:
-                    if not line.startswith(outdentString):
-
-                        # Only warn for doc comments, otherwise it might just be code commented out
-                        # which is sometimes formatted pretty crazy when commented out
-                        if self.variant == "doc":
-                            Console.warn("Invalid indentation in doc comment at line %s in %s", startLineNo+lineNo, self.fileId)
-
-                    else:
-                        lines[lineNo] = line[outdentStringLen:]
-
-        # Merge final lines and remove leading and trailing new lines
-        return "\n".join(lines).strip("\n")
-
-
-    def __processDoc(self, text, startLineNo):
-
-        text = self.__extractStaticType(text)
-        text = self.__extractReturns(text)
-        text = self.__extractTags(text)
-
-        # Collapse new empty lines at start/end
-        text = text.strip("\n\t ")
-
-        parsed = ''
-
-        # Now parse only the text outside of backticks
-        last = 0
-        def split(match):
-
-            # Grab the text before the back tick and process any parameters in it
-            nonlocal parsed
-            nonlocal last
-
-            start, end = match.span()
-            before = text[last:start]
-            parsed += self.__processParams(before) + match.group(1)
-            last = end
-
-        tickMatcher.sub(split, text)
-
-        # add the rest of the text
-        parsed += self.__processParams(text[last:])
-
-        text = self.__processLinks(parsed)
-
-        return text
-
-
-    def __splitTypeList(self, decl):
-
-        if decl is None:
-            return decl
-
-        splitted = listSplit.split(decl.strip())
-
-        result = []
-        for entry in splitted:
-
-            # Figure out if it is marked as array
-            isArray = False
-            if entry.endswith("[]"):
-                isArray = True
-                entry = entry[:-2]
-
-            store = {
-                "name" : entry
-            }
-
-            if isArray:
-                store["array"] = True
-
-            if entry in builtinTypes:
-                store["builtin"] = True
-
-            if entry in pseudoTypes:
-                store["pseudo"] = True
-
-            result.append(store)
-
-        return result
-
-
-
-    def __extractReturns(self, text):
-        """
-        Extracts leading return defintion (when type is function)
-        """
-
-        def collectReturn(match):
-            self.returns = self.__splitTypeList(match.group(1))
-            return ""
-
-        return returnMatcher.sub(collectReturn, text)
-
-
-
-    def __extractStaticType(self, text):
-        """
-        Extracts leading type defintion (when value is a static type)
-        """
-
-        def collectType(match):
-            self.type = match.group(1).strip()
-            return ""
-
-        return typeMatcher.sub(collectType, text)
-
-
-
-    def __extractTags(self, text):
-        """
-        Extract all tags inside the give doc comment. These are replaced from
-        the text and collected inside the "tags" key as a dict.
-        """
-
-        def collectTags(match):
-             if not self.tags:
-                 self.tags = {}
-
-             name = match.group(1)
-             param = match.group(3)
-
-             if name in self.tags:
-                 self.tags[name].add(param)
-             elif param:
-                 self.tags[name] = set([param])
-             else:
-                 self.tags[name] = True
-
-             return ""
-
-        return tagMatcher.sub(collectTags, text)
-
-
-    def __processParams(self, text):
-
-        def collectParams(match):
-
-            paramName = match.group(1)
-            paramTypes = match.group(3)
-            paramDynamic = match.group(4) is not None
-            paramOptional = match.group(5) is not None
-            paramDefault = match.group(7)
-
-            if paramTypes:
-                paramTypes = self.__splitTypeList(paramTypes)
-
-            if self.params is None:
-                self.params = {}
-
-            params = self.params
-            fullName = match.group(1).strip()
-            names = fullName.split('.')
-
-            for i, mapName in enumerate(names):
-
-                # Ensure we have the map object in the params
-                if not mapName in params:
-                    params[mapName] = {}
-
-                # Add new entries and overwrite if a type is defined in this entry
-                if not mapName in params or paramTypes is not None:
-
-                    # Make sure to not overwrite something like @options {Object} with the type of @options.x {Number}
-                    if i == len(names) - 1:
-
-                        paramEntry = params[mapName] = {}
-
-                        if paramTypes is not None:
-                            paramEntry["type"] = paramTypes
-
-                        if paramDynamic:
-                            paramEntry["dynamic"] = paramDynamic
-
-                        if paramOptional:
-                            paramEntry["optional"] = paramOptional
-
-                        if paramDefault is not None:
-                            paramEntry["default"] = paramDefault
-
-                    else:
-                        paramEntry = params[mapName]
-
-
-                else:
-                    paramEntry = params[mapName]
-
-                # create fields for new map level
-                if i + 1 < len(names):
-                    if not "fields" in paramEntry:
-                        paramEntry["fields"] = {}
-
-                    params = paramEntry["fields"]
-
-            return '<code class="param">%s</code>' % fullName
-
-        return paramMatcher.sub(collectParams, text)
-
-
-    def __processLinks(self, text):
-
-        def formatTypes(match):
-
-            parsedSection = match.group(3)
-            parsedFile = match.group(4)
-            parsedItem = match.group(6)
-
-            # Do not match {}
-            if parsedSection is None and parsedFile is None and parsedItem is None:
-                return match.group(1)
-
-            # Minor corrections
-            if parsedSection and not parsedItem:
-                parsedSection = ""
-
-            attr = ""
-            link = ""
-            label = ""
-
-            if parsedSection:
-                link += '%s:' % parsedSection
-
-            if parsedFile:
-                link += parsedFile
-                label += parsedFile
-
-            if parsedItem:
-                link += "~%s" % parsedItem
-                if label == "":
-                    label = parsedItem
-                else:
-                    label += "#%s" % parsedItem
-
-            # add link to attributes list
-            attr += ' href="#%s"' % link
-
-            # build final HTML
-            return '<a%s><code>%s</code></a>' % (attr, label)
-
-        return linkMatcher.sub(formatTypes, text)
-

eric ide

mercurial