QScintilla/Lexers/LexerPygments.py

Wed, 05 Jan 2011 15:46:19 +0100

author
Detlev Offenbach <detlev@die-offenbachs.de>
date
Wed, 05 Jan 2011 15:46:19 +0100
changeset 808
8f85926125ef
parent 791
9ec2ac20e54e
child 815
093b3b8204d1
permissions
-rw-r--r--

Updated Pygments to version 1.4.0.

# -*- coding: utf-8 -*-

# Copyright (c) 2008 - 2011 Detlev Offenbach <detlev@die-offenbachs.de>
#

"""
Module implementing a custom lexer using pygments.
"""

from pygments.token import Token
from pygments.lexers import guess_lexer_for_filename, guess_lexer, find_lexer_class
from pygments.util import ClassNotFound

from PyQt4.QtGui import QColor, QFont

from QScintilla.Lexers.LexerContainer import LexerContainer

import Utilities

PYGMENTS_DEFAULT, \
PYGMENTS_COMMENT, \
PYGMENTS_PREPROCESSOR, \
PYGMENTS_KEYWORD, \
PYGMENTS_PSEUDOKEYWORD, \
PYGMENTS_TYPEKEYWORD, \
PYGMENTS_OPERATOR, \
PYGMENTS_WORD, \
PYGMENTS_BUILTIN, \
PYGMENTS_FUNCTION, \
PYGMENTS_CLASS, \
PYGMENTS_NAMESPACE, \
PYGMENTS_EXCEPTION, \
PYGMENTS_VARIABLE, \
PYGMENTS_CONSTANT, \
PYGMENTS_LABEL, \
PYGMENTS_ENTITY, \
PYGMENTS_ATTRIBUTE, \
PYGMENTS_TAG, \
PYGMENTS_DECORATOR, \
PYGMENTS_STRING, \
PYGMENTS_DOCSTRING, \
PYGMENTS_SCALAR, \
PYGMENTS_ESCAPE, \
PYGMENTS_REGEX, \
PYGMENTS_SYMBOL, \
PYGMENTS_OTHER, \
PYGMENTS_NUMBER, \
PYGMENTS_HEADING, \
PYGMENTS_SUBHEADING, \
PYGMENTS_DELETED, \
PYGMENTS_INSERTED           = list(range(32))
# 32 to 39 are reserved for QScintilla internal styles
PYGMENTS_GENERIC_ERROR, \
PYGMENTS_EMPHASIZE, \
PYGMENTS_STRONG, \
PYGMENTS_PROMPT, \
PYGMENTS_OUTPUT, \
PYGMENTS_TRACEBACK, \
PYGMENTS_ERROR, \
PYGMENTS_MULTILINECOMMENT, \
PYGMENTS_PROPERTY, \
PYGMENTS_CHAR, \
PYGMENTS_HEREDOC, \
PYGMENTS_PUNCTUATION        = list(range(40, 52))

#-----------------------------------------------------------------------------#

TOKEN_MAP = {
    Token.Comment:                  PYGMENTS_COMMENT,
    Token.Comment.Preproc:          PYGMENTS_PREPROCESSOR,
    Token.Comment.Multiline:        PYGMENTS_MULTILINECOMMENT,
    Token.Comment.Single:           PYGMENTS_COMMENT,
    Token.Comment.Special:          PYGMENTS_COMMENT,

    Token.Keyword:                  PYGMENTS_KEYWORD,
    Token.Keyword.Pseudo:           PYGMENTS_PSEUDOKEYWORD,
    Token.Keyword.Type:             PYGMENTS_TYPEKEYWORD,
    Token.Keyword.Namespace:        PYGMENTS_KEYWORD,

    Token.Operator:                 PYGMENTS_OPERATOR,
    Token.Operator.Word:            PYGMENTS_WORD,

    Token.Name:                     PYGMENTS_DEFAULT,
    Token.Name.Builtin:             PYGMENTS_BUILTIN,
    Token.Name.Builtin.Pseudo:      PYGMENTS_BUILTIN,
    Token.Name.Function:            PYGMENTS_FUNCTION,
    Token.Name.Class:               PYGMENTS_CLASS,
    Token.Name.Namespace:           PYGMENTS_NAMESPACE,
    Token.Name.Exception:           PYGMENTS_EXCEPTION,
    Token.Name.Variable:            PYGMENTS_VARIABLE,
    Token.Name.Variable.Class:      PYGMENTS_VARIABLE,
    Token.Name.Variable.Global:     PYGMENTS_VARIABLE,
    Token.Name.Variable.Instance:   PYGMENTS_VARIABLE,
    Token.Name.Constant:            PYGMENTS_CONSTANT,
    Token.Name.Label:               PYGMENTS_LABEL,
    Token.Name.Entity:              PYGMENTS_ENTITY,
    Token.Name.Attribute:           PYGMENTS_ATTRIBUTE,
    Token.Name.Tag:                 PYGMENTS_TAG,
    Token.Name.Decorator:           PYGMENTS_DECORATOR,
    Token.Name.Property:            PYGMENTS_PROPERTY,

    Token.String:                   PYGMENTS_STRING,
    Token.String.Char:              PYGMENTS_CHAR,
    Token.String.Doc:               PYGMENTS_DOCSTRING,
    Token.String.Interpol:          PYGMENTS_SCALAR,
    Token.String.Escape:            PYGMENTS_ESCAPE,
    Token.String.Regex:             PYGMENTS_REGEX,
    Token.String.Symbol:            PYGMENTS_SYMBOL,
    Token.String.Other:             PYGMENTS_OTHER,
    Token.String.Heredoc:           PYGMENTS_HEREDOC,
    
    Token.Number:                   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.Punctuation:              PYGMENTS_PUNCTUATION,

    Token.Generic.Heading:          PYGMENTS_HEADING,
    Token.Generic.Subheading:       PYGMENTS_SUBHEADING,
    Token.Generic.Deleted:          PYGMENTS_DELETED,
    Token.Generic.Inserted:         PYGMENTS_INSERTED,
    Token.Generic.Error:            PYGMENTS_GENERIC_ERROR,
    Token.Generic.Emph:             PYGMENTS_EMPHASIZE,
    Token.Generic.Strong:           PYGMENTS_STRONG,
    Token.Generic.Prompt:           PYGMENTS_PROMPT,
    Token.Generic.Output:           PYGMENTS_OUTPUT,
    Token.Generic.Traceback:        PYGMENTS_TRACEBACK,

    Token.Error:                    PYGMENTS_ERROR, 
}

#-----------------------------------------------------------------------------#

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.trUtf8("Default"), 
            PYGMENTS_COMMENT            : self.trUtf8("Comment"), 
            PYGMENTS_PREPROCESSOR       : self.trUtf8("Preprocessor"), 
            PYGMENTS_KEYWORD            : self.trUtf8("Keyword"), 
            PYGMENTS_PSEUDOKEYWORD      : self.trUtf8("Pseudo Keyword"), 
            PYGMENTS_TYPEKEYWORD        : self.trUtf8("Type Keyword"), 
            PYGMENTS_OPERATOR           : self.trUtf8("Operator"), 
            PYGMENTS_WORD               : self.trUtf8("Word"), 
            PYGMENTS_BUILTIN            : self.trUtf8("Builtin"), 
            PYGMENTS_FUNCTION           : self.trUtf8("Function or method name"), 
            PYGMENTS_CLASS              : self.trUtf8("Class name"), 
            PYGMENTS_NAMESPACE          : self.trUtf8("Namespace"), 
            PYGMENTS_EXCEPTION          : self.trUtf8("Exception"), 
            PYGMENTS_VARIABLE           : self.trUtf8("Identifier"), 
            PYGMENTS_CONSTANT           : self.trUtf8("Constant"), 
            PYGMENTS_LABEL              : self.trUtf8("Label"), 
            PYGMENTS_ENTITY             : self.trUtf8("Entity"), 
            PYGMENTS_ATTRIBUTE          : self.trUtf8("Attribute"), 
            PYGMENTS_TAG                : self.trUtf8("Tag"), 
            PYGMENTS_DECORATOR          : self.trUtf8("Decorator"), 
            PYGMENTS_STRING             : self.trUtf8("String"), 
            PYGMENTS_DOCSTRING          : self.trUtf8("Documentation string"), 
            PYGMENTS_SCALAR             : self.trUtf8("Scalar"), 
            PYGMENTS_ESCAPE             : self.trUtf8("Escape"), 
            PYGMENTS_REGEX              : self.trUtf8("Regular expression"), 
            PYGMENTS_SYMBOL             : self.trUtf8("Symbol"), 
            PYGMENTS_OTHER              : self.trUtf8("Other string"), 
            PYGMENTS_NUMBER             : self.trUtf8("Number"), 
            PYGMENTS_HEADING            : self.trUtf8("Heading"), 
            PYGMENTS_SUBHEADING         : self.trUtf8("Subheading"), 
            PYGMENTS_DELETED            : self.trUtf8("Deleted"), 
            PYGMENTS_INSERTED           : self.trUtf8("Inserted"), 
            PYGMENTS_GENERIC_ERROR      : self.trUtf8("Generic error"), 
            PYGMENTS_EMPHASIZE          : self.trUtf8("Emphasized text"), 
            PYGMENTS_STRONG             : self.trUtf8("Strong text"), 
            PYGMENTS_PROMPT             : self.trUtf8("Prompt"), 
            PYGMENTS_OUTPUT             : self.trUtf8("Output"), 
            PYGMENTS_TRACEBACK          : self.trUtf8("Traceback"), 
            PYGMENTS_ERROR              : self.trUtf8("Error"), 
            PYGMENTS_MULTILINECOMMENT   : self.trUtf8("Comment block"),
            PYGMENTS_PROPERTY           : self.trUtf8("Property"),
            PYGMENTS_CHAR               : self.trUtf8("Character"),
            PYGMENTS_HEREDOC            : self.trUtf8("Here document"),
            PYGMENTS_PUNCTUATION        : self.trUtf8("Punctuation"),
        }
        
        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"),
        }
        
        self.defaultPapers = {
            PYGMENTS_ERROR              : QColor("#FF0000"), 
            PYGMENTS_MULTILINECOMMENT   : QColor("#A8FFA8"),
            PYGMENTS_HEREDOC            : QColor("#DDD0DD"),
        }
        
        self.defaultEolFill = {
            PYGMENTS_ERROR              : True, 
            PYGMENTS_MULTILINECOMMENT   : True,
            PYGMENTS_HEREDOC            : True, 
        }
    
    def language(self):
        """
        Public method returning the language of the lexer.
        
        @return language of the lexer (string)
        """
        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)
            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)
            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.defaultEolFill[style]
        except KeyError:
            return LexerContainer.defaultEolFill(self, style)
        
    def styleBitsNeeded(self):
        """
        Public method to get the number of style bits needed by the lexer.
        
        @return number of style bits needed (integer)
        """
        return 6
    
    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()
                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]
        self.__lexer = self.__guessLexer(text)
        
        cpos = 0
        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')
                if tlen:
                    self.editor.setStyling(tlen, style)
                cpos += tlen
            self.editor.startStyling(cpos, 0x3f)
    
    def isCommentStyle(self, style):
        """
        Public method to check, if a style is a comment style.
        
        @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.
        
        @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

eric ide

mercurial