eric6/QScintilla/Lexers/LexerPygments.py

changeset 6942
2602857055c5
parent 6891
93f82da09f22
child 7229
53054eb5b15a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/eric6/QScintilla/Lexers/LexerPygments.py	Sun Apr 14 15:09:21 2019 +0200
@@ -0,0 +1,525 @@
+# -*- coding: utf-8 -*-
+
+# Copyright (c) 2008 - 2019 Detlev Offenbach <detlev@die-offenbachs.de>
+#
+
+"""
+Module implementing a custom lexer using pygments.
+"""
+
+from __future__ import unicode_literals
+
+from pygments.token import Token
+from pygments.lexers import guess_lexer_for_filename, guess_lexer, \
+    find_lexer_class
+from pygments.util import ClassNotFound
+
+from PyQt5.QtGui import QColor, QFont
+
+from QScintilla.Lexers.LexerContainer import LexerContainer
+
+import Utilities
+
+PYGMENTS_DEFAULT = 0
+PYGMENTS_COMMENT = 1
+PYGMENTS_PREPROCESSOR = 2
+PYGMENTS_KEYWORD = 3
+PYGMENTS_PSEUDOKEYWORD = 4
+PYGMENTS_TYPEKEYWORD = 5
+PYGMENTS_OPERATOR = 6
+PYGMENTS_WORD = 7
+PYGMENTS_BUILTIN = 8
+PYGMENTS_FUNCTION = 9
+PYGMENTS_CLASS = 10
+PYGMENTS_NAMESPACE = 11
+PYGMENTS_EXCEPTION = 12
+PYGMENTS_VARIABLE = 13
+PYGMENTS_CONSTANT = 14
+PYGMENTS_LABEL = 15
+PYGMENTS_ENTITY = 16
+PYGMENTS_ATTRIBUTE = 17
+PYGMENTS_TAG = 18
+PYGMENTS_DECORATOR = 19
+PYGMENTS_STRING = 20
+PYGMENTS_DOCSTRING = 21
+PYGMENTS_SCALAR = 22
+PYGMENTS_ESCAPE = 23
+PYGMENTS_REGEX = 24
+PYGMENTS_SYMBOL = 25
+PYGMENTS_OTHER = 26
+PYGMENTS_NUMBER = 27
+PYGMENTS_HEADING = 28
+PYGMENTS_SUBHEADING = 29
+PYGMENTS_DELETED = 30
+PYGMENTS_INSERTED = 31
+# 32 to 39 are reserved for QScintilla internal styles
+PYGMENTS_GENERIC_ERROR = 40
+PYGMENTS_EMPHASIZE = 41
+PYGMENTS_STRONG = 42
+PYGMENTS_PROMPT = 43
+PYGMENTS_OUTPUT = 44
+PYGMENTS_TRACEBACK = 45
+PYGMENTS_ERROR = 46
+PYGMENTS_MULTILINECOMMENT = 47
+PYGMENTS_PROPERTY = 48
+PYGMENTS_CHAR = 49
+PYGMENTS_HEREDOC = 50
+PYGMENTS_PUNCTUATION = 51
+# added with Pygments 2.1
+PYGMENTS_HASHBANG = 52
+PYGMENTS_RESERVEDKEYWORD = 53
+PYGMENTS_LITERAL = 54
+PYGMENTS_DOUBLESTRING = 55
+PYGMENTS_SINGLESTRING = 56
+PYGMENTS_BACKTICKSTRING = 57
+PYGMENTS_WHITESPACE = 58
+
+#-----------------------------------------------------------------------------#
+
+TOKEN_MAP = {
+    Token.Comment: PYGMENTS_COMMENT,
+    Token.Comment.Hashbang: PYGMENTS_HASHBANG,
+    Token.Comment.Multiline: PYGMENTS_MULTILINECOMMENT,
+    Token.Comment.Preproc: PYGMENTS_PREPROCESSOR,
+    Token.Comment.PreprocFile: PYGMENTS_PREPROCESSOR,
+    Token.Comment.Single: PYGMENTS_COMMENT,
+    Token.Comment.Special: PYGMENTS_COMMENT,
+    
+    Token.Error: PYGMENTS_ERROR,
+    
+    Token.Generic.Deleted: PYGMENTS_DELETED,
+    Token.Generic.Emph: PYGMENTS_EMPHASIZE,
+    Token.Generic.Error: PYGMENTS_GENERIC_ERROR,
+    Token.Generic.Heading: PYGMENTS_HEADING,
+    Token.Generic.Inserted: PYGMENTS_INSERTED,
+    Token.Generic.Output: PYGMENTS_OUTPUT,
+    Token.Generic.Prompt: PYGMENTS_PROMPT,
+    Token.Generic.Strong: PYGMENTS_STRONG,
+    Token.Generic.Subheading: PYGMENTS_SUBHEADING,
+    Token.Generic.Traceback: PYGMENTS_TRACEBACK,
+
+    Token.Keyword: PYGMENTS_KEYWORD,
+    Token.Keyword.Constant: PYGMENTS_KEYWORD,
+    Token.Keyword.Declaration: PYGMENTS_KEYWORD,
+    Token.Keyword.Namespace: PYGMENTS_KEYWORD,
+    Token.Keyword.Pseudo: PYGMENTS_PSEUDOKEYWORD,
+    Token.Keyword.Reserved: PYGMENTS_RESERVEDKEYWORD,
+    Token.Keyword.Type: PYGMENTS_TYPEKEYWORD,
+
+    Token.Literal: PYGMENTS_LITERAL,
+    Token.Literal.Date: PYGMENTS_LITERAL,
+
+    Token.Name: PYGMENTS_DEFAULT,
+    Token.Name.Attribute: PYGMENTS_ATTRIBUTE,
+    Token.Name.Builtin: PYGMENTS_BUILTIN,
+    Token.Name.Builtin.Pseudo: PYGMENTS_BUILTIN,
+    Token.Name.Class: PYGMENTS_CLASS,
+    Token.Name.Constant: PYGMENTS_CONSTANT,
+    Token.Name.Decorator: PYGMENTS_DECORATOR,
+    Token.Name.Entity: PYGMENTS_ENTITY,
+    Token.Name.Exception: PYGMENTS_EXCEPTION,
+    Token.Name.Function: PYGMENTS_FUNCTION,
+    Token.Name.Label: PYGMENTS_LABEL,
+    Token.Name.Namespace: PYGMENTS_NAMESPACE,
+    Token.Name.Other: PYGMENTS_VARIABLE,
+    Token.Name.Property: PYGMENTS_PROPERTY,
+    Token.Name.Tag: PYGMENTS_TAG,
+    Token.Name.Variable: PYGMENTS_VARIABLE,
+    Token.Name.Variable.Class: PYGMENTS_VARIABLE,
+    Token.Name.Variable.Global: PYGMENTS_VARIABLE,
+    Token.Name.Variable.Instance: PYGMENTS_VARIABLE,
+
+    Token.Number: PYGMENTS_NUMBER,
+    Token.Number.Bin: PYGMENTS_NUMBER,
+    Token.Number.Float: PYGMENTS_NUMBER,
+    Token.Number.Hex: PYGMENTS_NUMBER,
+    Token.Number.Integer: PYGMENTS_NUMBER,
+    Token.Number.Integer.Long: PYGMENTS_NUMBER,
+    Token.Number.Oct: PYGMENTS_NUMBER,
+
+    Token.Operator: PYGMENTS_OPERATOR,
+    Token.Operator.Word: PYGMENTS_WORD,
+
+    Token.Punctuation: PYGMENTS_PUNCTUATION,
+
+    Token.String: PYGMENTS_STRING,
+    Token.String.Backtick: PYGMENTS_BACKTICKSTRING,
+    Token.String.Char: PYGMENTS_CHAR,
+    Token.String.Doc: PYGMENTS_DOCSTRING,
+    Token.String.Double: PYGMENTS_DOUBLESTRING,
+    Token.String.Escape: PYGMENTS_ESCAPE,
+    Token.String.Heredoc: PYGMENTS_HEREDOC,
+    Token.String.Interpol: PYGMENTS_SCALAR,
+    Token.String.Other: PYGMENTS_OTHER,
+    Token.String.Regex: PYGMENTS_REGEX,
+    Token.String.Single: PYGMENTS_SINGLESTRING,
+    Token.String.Symbol: PYGMENTS_SYMBOL,
+    
+    Token.Whitespace: PYGMENTS_WHITESPACE,
+}
+
+#-----------------------------------------------------------------------------#
+
+
+class LexerPygments(LexerContainer):
+    """
+    Class implementing a custom lexer using pygments.
+    """
+    def __init__(self, parent=None, name=""):
+        """
+        Constructor
+        
+        @param parent parent widget of this lexer
+        @keyparam name name of the pygments lexer to use (string)
+        """
+        LexerContainer.__init__(self, parent)
+        
+        self.__pygmentsName = name
+        
+        self.descriptions = {
+            PYGMENTS_DEFAULT: self.tr("Default"),
+            PYGMENTS_COMMENT: self.tr("Comment"),
+            PYGMENTS_PREPROCESSOR: self.tr("Preprocessor"),
+            PYGMENTS_KEYWORD: self.tr("Keyword"),
+            PYGMENTS_PSEUDOKEYWORD: self.tr("Pseudo Keyword"),
+            PYGMENTS_TYPEKEYWORD: self.tr("Type Keyword"),
+            PYGMENTS_OPERATOR: self.tr("Operator"),
+            PYGMENTS_WORD: self.tr("Word"),
+            PYGMENTS_BUILTIN: self.tr("Builtin"),
+            PYGMENTS_FUNCTION: self.tr("Function or method name"),
+            PYGMENTS_CLASS: self.tr("Class name"),
+            PYGMENTS_NAMESPACE: self.tr("Namespace"),
+            PYGMENTS_EXCEPTION: self.tr("Exception"),
+            PYGMENTS_VARIABLE: self.tr("Identifier"),
+            PYGMENTS_CONSTANT: self.tr("Constant"),
+            PYGMENTS_LABEL: self.tr("Label"),
+            PYGMENTS_ENTITY: self.tr("Entity"),
+            PYGMENTS_ATTRIBUTE: self.tr("Attribute"),
+            PYGMENTS_TAG: self.tr("Tag"),
+            PYGMENTS_DECORATOR: self.tr("Decorator"),
+            PYGMENTS_STRING: self.tr("String"),
+            PYGMENTS_DOCSTRING: self.tr("Documentation string"),
+            PYGMENTS_SCALAR: self.tr("Scalar"),
+            PYGMENTS_ESCAPE: self.tr("Escape"),
+            PYGMENTS_REGEX: self.tr("Regular expression"),
+            PYGMENTS_SYMBOL: self.tr("Symbol"),
+            PYGMENTS_OTHER: self.tr("Other string"),
+            PYGMENTS_NUMBER: self.tr("Number"),
+            PYGMENTS_HEADING: self.tr("Heading"),
+            PYGMENTS_SUBHEADING: self.tr("Subheading"),
+            PYGMENTS_DELETED: self.tr("Deleted"),
+            PYGMENTS_INSERTED: self.tr("Inserted"),
+            PYGMENTS_GENERIC_ERROR: self.tr("Generic error"),
+            PYGMENTS_EMPHASIZE: self.tr("Emphasized text"),
+            PYGMENTS_STRONG: self.tr("Strong text"),
+            PYGMENTS_PROMPT: self.tr("Prompt"),
+            PYGMENTS_OUTPUT: self.tr("Output"),
+            PYGMENTS_TRACEBACK: self.tr("Traceback"),
+            PYGMENTS_ERROR: self.tr("Error"),
+            PYGMENTS_MULTILINECOMMENT: self.tr("Comment block"),
+            PYGMENTS_PROPERTY: self.tr("Property"),
+            PYGMENTS_CHAR: self.tr("Character"),
+            PYGMENTS_HEREDOC: self.tr("Here document"),
+            PYGMENTS_PUNCTUATION: self.tr("Punctuation"),
+            PYGMENTS_HASHBANG: self.tr("Hashbang"),
+            PYGMENTS_RESERVEDKEYWORD: self.tr("Reserved Keyword"),
+            PYGMENTS_LITERAL: self.tr("Literal"),
+            PYGMENTS_DOUBLESTRING: self.tr("Double quoted string"),
+            PYGMENTS_SINGLESTRING: self.tr("Single quoted string"),
+            PYGMENTS_BACKTICKSTRING: self.tr("Backtick string"),
+            PYGMENTS_WHITESPACE: self.tr("Whitespace"),
+        }
+        
+        self.defaultColors = {
+            PYGMENTS_DEFAULT: QColor("#000000"),
+            PYGMENTS_COMMENT: QColor("#408080"),
+            PYGMENTS_PREPROCESSOR: QColor("#BC7A00"),
+            PYGMENTS_KEYWORD: QColor("#008000"),
+            PYGMENTS_PSEUDOKEYWORD: QColor("#008000"),
+            PYGMENTS_TYPEKEYWORD: QColor("#B00040"),
+            PYGMENTS_OPERATOR: QColor("#666666"),
+            PYGMENTS_WORD: QColor("#AA22FF"),
+            PYGMENTS_BUILTIN: QColor("#008000"),
+            PYGMENTS_FUNCTION: QColor("#0000FF"),
+            PYGMENTS_CLASS: QColor("#0000FF"),
+            PYGMENTS_NAMESPACE: QColor("#0000FF"),
+            PYGMENTS_EXCEPTION: QColor("#D2413A"),
+            PYGMENTS_VARIABLE: QColor("#19177C"),
+            PYGMENTS_CONSTANT: QColor("#880000"),
+            PYGMENTS_LABEL: QColor("#A0A000"),
+            PYGMENTS_ENTITY: QColor("#999999"),
+            PYGMENTS_ATTRIBUTE: QColor("#7D9029"),
+            PYGMENTS_TAG: QColor("#008000"),
+            PYGMENTS_DECORATOR: QColor("#AA22FF"),
+            PYGMENTS_STRING: QColor("#BA2121"),
+            PYGMENTS_DOCSTRING: QColor("#BA2121"),
+            PYGMENTS_SCALAR: QColor("#BB6688"),
+            PYGMENTS_ESCAPE: QColor("#BB6622"),
+            PYGMENTS_REGEX: QColor("#BB6688"),
+            PYGMENTS_SYMBOL: QColor("#19177C"),
+            PYGMENTS_OTHER: QColor("#008000"),
+            PYGMENTS_NUMBER: QColor("#666666"),
+            PYGMENTS_HEADING: QColor("#000080"),
+            PYGMENTS_SUBHEADING: QColor("#800080"),
+            PYGMENTS_DELETED: QColor("#A00000"),
+            PYGMENTS_INSERTED: QColor("#00A000"),
+            PYGMENTS_GENERIC_ERROR: QColor("#FF0000"),
+            PYGMENTS_PROMPT: QColor("#000080"),
+            PYGMENTS_OUTPUT: QColor("#808080"),
+            PYGMENTS_TRACEBACK: QColor("#0040D0"),
+            PYGMENTS_MULTILINECOMMENT: QColor("#007F00"),
+            PYGMENTS_PROPERTY: QColor("#00A0E0"),
+            PYGMENTS_CHAR: QColor("#7F007F"),
+            PYGMENTS_HEREDOC: QColor("#7F007F"),
+            PYGMENTS_PUNCTUATION: QColor("#000000"),
+            PYGMENTS_HASHBANG: QColor("#00C000"),
+            PYGMENTS_RESERVEDKEYWORD: QColor("#A90D91"),
+            PYGMENTS_LITERAL: QColor("#1C01CE"),
+            PYGMENTS_DOUBLESTRING: QColor("#7F007F"),
+            PYGMENTS_SINGLESTRING: QColor("#7F007F"),
+            PYGMENTS_BACKTICKSTRING: QColor("#FFFF00"),
+            PYGMENTS_WHITESPACE: QColor("#BBBBBB"),
+        }
+        
+        self.defaultPapers = {
+            PYGMENTS_ERROR: QColor("#FF0000"),
+            PYGMENTS_MULTILINECOMMENT: QColor("#A8FFA8"),
+            PYGMENTS_HEREDOC: QColor("#DDD0DD"),
+            PYGMENTS_BACKTICKSTRING: QColor("#a08080"),
+        }
+        
+        self.defaultEolFills = {
+            PYGMENTS_ERROR: True,
+            PYGMENTS_MULTILINECOMMENT: True,
+            PYGMENTS_HEREDOC: True,
+            PYGMENTS_BACKTICKSTRING: True,
+        }
+    
+    def language(self):
+        """
+        Public method returning the language of the lexer.
+        
+        @return language of the lexer (string)
+        """
+        if self.__pygmentsName:
+            return self.__pygmentsName
+        else:
+            return "Guessed"
+    
+    def description(self, style):
+        """
+        Public method returning the descriptions of the styles supported
+        by the lexer.
+        
+        @param style style number (integer)
+        @return description for the style (string)
+        """
+        try:
+            return self.descriptions[style]
+        except KeyError:
+            return ""
+    
+    def defaultColor(self, style):
+        """
+        Public method to get the default foreground color for a style.
+        
+        @param style style number (integer)
+        @return foreground color (QColor)
+        """
+        try:
+            return self.defaultColors[style]
+        except KeyError:
+            return LexerContainer.defaultColor(self, style)
+    
+    def defaultPaper(self, style):
+        """
+        Public method to get the default background color for a style.
+        
+        @param style style number (integer)
+        @return background color (QColor)
+        """
+        try:
+            return self.defaultPapers[style]
+        except KeyError:
+            return LexerContainer.defaultPaper(self, style)
+    
+    def defaultFont(self, style):
+        """
+        Public method to get the default font for a style.
+        
+        @param style style number (integer)
+        @return font (QFont)
+        """
+        if style in [PYGMENTS_COMMENT, PYGMENTS_PREPROCESSOR,
+                     PYGMENTS_MULTILINECOMMENT]:
+            if Utilities.isWindowsPlatform():
+                f = QFont("Comic Sans MS", 9)
+            elif Utilities.isMacPlatform():
+                f = QFont("Courier", 11)
+            else:
+                f = QFont("Bitstream Vera Serif", 9)
+            if style == PYGMENTS_PREPROCESSOR:
+                f.setItalic(True)
+            return f
+        
+        if style in [PYGMENTS_STRING, PYGMENTS_CHAR]:
+            if Utilities.isWindowsPlatform():
+                return QFont("Comic Sans MS", 10)
+            elif Utilities.isMacPlatform():
+                f = QFont("Courier", 11)
+            else:
+                return QFont("Bitstream Vera Serif", 10)
+        
+        if style in [PYGMENTS_KEYWORD, PYGMENTS_OPERATOR, PYGMENTS_WORD,
+                     PYGMENTS_BUILTIN, PYGMENTS_ATTRIBUTE, PYGMENTS_FUNCTION,
+                     PYGMENTS_CLASS, PYGMENTS_NAMESPACE, PYGMENTS_EXCEPTION,
+                     PYGMENTS_ENTITY, PYGMENTS_TAG, PYGMENTS_SCALAR,
+                     PYGMENTS_ESCAPE, PYGMENTS_HEADING, PYGMENTS_SUBHEADING,
+                     PYGMENTS_STRONG, PYGMENTS_PROMPT]:
+            f = LexerContainer.defaultFont(self, style)
+            f.setBold(True)
+            return f
+        
+        if style in [PYGMENTS_DOCSTRING, PYGMENTS_EMPHASIZE]:
+            f = LexerContainer.defaultFont(self, style)
+            f.setItalic(True)
+            return f
+        
+        return LexerContainer.defaultFont(self, style)
+    
+    def defaultEolFill(self, style):
+        """
+        Public method to get the default fill to eol flag.
+        
+        @param style style number (integer)
+        @return fill to eol flag (boolean)
+        """
+        try:
+            return self.defaultEolFills[style]
+        except KeyError:
+            return LexerContainer.defaultEolFill(self, style)
+        
+    def __guessLexer(self, text):
+        """
+        Private method to guess a pygments lexer.
+        
+        @param text text to base guessing on (string)
+        @return reference to the guessed lexer (pygments.lexer)
+        """
+        lexer = None
+        
+        if self.__pygmentsName:
+            lexerClass = find_lexer_class(self.__pygmentsName)
+            if lexerClass is not None:
+                lexer = lexerClass()
+        
+        elif text:
+            # step 1: guess based on filename and text
+            if self.editor is not None:
+                fn = self.editor.getFileName()
+                if fn:
+                    try:
+                        lexer = guess_lexer_for_filename(fn, text)
+                    except ClassNotFound:
+                        pass
+            
+            # step 2: guess on text only
+            if lexer is None:
+                try:
+                    lexer = guess_lexer(text)
+                except ClassNotFound:
+                    pass
+        
+        return lexer
+    
+    def canStyle(self):
+        """
+        Public method to check, if the lexer is able to style the text.
+        
+        @return flag indicating the lexer capability (boolean)
+        """
+        if self.editor is None:
+            return True
+        
+        text = self.editor.text()
+        self.__lexer = self.__guessLexer(text)
+        
+        return self.__lexer is not None
+    
+    def name(self):
+        """
+        Public method to get the name of the pygments lexer.
+        
+        @return name of the pygments lexer (string)
+        """
+        if self.__lexer is None:
+            return ""
+        else:
+            return self.__lexer.name
+    
+    def styleText(self, start, end):
+        """
+        Public method to perform the styling.
+        
+        @param start position of first character to be styled (integer)
+        @param end position of last character to be styled (integer)
+        """
+        text = self.editor.text()[:end + 1]
+        textLen = len(text.encode("utf-8"))
+        self.__lexer = self.__guessLexer(text)
+        
+        cpos = 0
+        # adjust start position because pygments ignores empty line at
+        # start of text
+        for c in text:
+            if c == "\n":
+                cpos += 1
+            else:
+                break
+        
+        self.editor.startStyling(cpos, 0x3f)
+        if self.__lexer is None:
+            self.editor.setStyling(len(text), PYGMENTS_DEFAULT)
+        else:
+            eolLen = len(self.editor.getLineSeparator())
+            for token, txt in self.__lexer.get_tokens(text):
+                style = TOKEN_MAP.get(token, PYGMENTS_DEFAULT)
+                
+                tlen = len(txt.encode('utf-8'))
+                if eolLen > 1:
+                    tlen += txt.count('\n')
+                cpos += tlen
+                if tlen and cpos < textLen:
+                    self.editor.setStyling(tlen, style)
+                if cpos >= textLen:
+                    break
+            self.editor.startStyling(cpos, 0x3f)
+    
+    def isCommentStyle(self, style):
+        """
+        Public method to check, if a style is a comment style.
+        
+        @param style style to check (integer)
+        @return flag indicating a comment style (boolean)
+        """
+        return style in [PYGMENTS_COMMENT]
+    
+    def isStringStyle(self, style):
+        """
+        Public method to check, if a style is a string style.
+        
+        @param style style to check (integer)
+        @return flag indicating a string style (boolean)
+        """
+        return style in [PYGMENTS_STRING, PYGMENTS_DOCSTRING, PYGMENTS_OTHER,
+                         PYGMENTS_HEADING, PYGMENTS_SUBHEADING,
+                         PYGMENTS_EMPHASIZE, PYGMENTS_STRONG]
+    
+    def defaultKeywords(self, kwSet):
+        """
+        Public method to get the default keywords.
+        
+        @param kwSet number of the keyword set (integer)
+        @return string giving the keywords (string) or None
+        """
+        return None     # __IGNORE_WARNING_M831__

eric ide

mercurial