CoolFace
Apppublic

Aluode/PerceptionLabPortable

sourceHugging Faceupdated 9mo agoView on Hugging Face
0likes
syntax.py265 linesDownload Raw Back to examples
1# based on https://github.com/art1415926535/PyQt5-syntax-highlighting2 3import pyqtgraph as pg4from pyqtgraph.Qt import QtCore, QtGui, QtWidgets5 6QRegExp = QtCore.QRegExp7 8QFont = QtGui.QFont9QColor = QtGui.QColor10QTextCharFormat = QtGui.QTextCharFormat11QSyntaxHighlighter = QtGui.QSyntaxHighlighter12 13 14def format(color, style=''):15    """16    Return a QTextCharFormat with the given attributes.17    """18    _color = pg.functions.mkColor(color)19 20    _format = QTextCharFormat()21    _format.setForeground(_color)22    if 'bold' in style:23        _format.setFontWeight(QFont.Weight.Bold)24    if 'italic' in style:25        _format.setFontItalic(True)26 27    return _format28 29 30class LightThemeColors:31 32    Red = "#B71C1C"33    Pink = "#FCE4EC"34    Purple = "#4A148C"35    DeepPurple = "#311B92"36    Indigo = "#1A237E"37    Blue = "#0D47A1"38    LightBlue = "#01579B"39    Cyan = "#006064"40    Teal = "#004D40"41    Green = "#1B5E20"42    LightGreen = "#33691E"43    Lime = "#827717"44    Yellow = "#F57F17"45    Amber = "#FF6F00"46    Orange = "#E65100"47    DeepOrange = "#BF360C"48    Brown = "#3E2723"49    Grey = "#212121"50    BlueGrey = "#263238"51 52 53class DarkThemeColors:54 55    Red = "#F44336"56    Pink = "#F48FB1"57    Purple = "#CE93D8"58    DeepPurple = "#B39DDB"59    Indigo = "#9FA8DA"60    Blue = "#90CAF9"61    LightBlue = "#81D4FA"62    Cyan = "#80DEEA"63    Teal = "#80CBC4"64    Green = "#A5D6A7"65    LightGreen = "#C5E1A5"66    Lime = "#E6EE9C"67    Yellow = "#FFF59D"68    Amber = "#FFE082"69    Orange = "#FFCC80"70    DeepOrange = "#FFAB91"71    Brown = "#BCAAA4"72    Grey = "#EEEEEE"73    BlueGrey = "#B0BEC5"74 75 76LIGHT_STYLES = {77    'keyword': format(LightThemeColors.Blue, 'bold'),78    'operator': format(LightThemeColors.Red, 'bold'),79    'brace': format(LightThemeColors.Purple),80    'defclass': format(LightThemeColors.Indigo, 'bold'),81    'string': format(LightThemeColors.Amber),82    'string2': format(LightThemeColors.DeepPurple),83    'comment': format(LightThemeColors.Green, 'italic'),84    'self': format(LightThemeColors.Blue, 'bold'),85    'numbers': format(LightThemeColors.Teal),86}87 88 89DARK_STYLES = {90    'keyword': format(DarkThemeColors.Blue, 'bold'),91    'operator': format(DarkThemeColors.Red, 'bold'),92    'brace': format(DarkThemeColors.Purple),93    'defclass': format(DarkThemeColors.Indigo, 'bold'),94    'string': format(DarkThemeColors.Amber),95    'string2': format(DarkThemeColors.DeepPurple),96    'comment': format(DarkThemeColors.Green, 'italic'),97    'self': format(DarkThemeColors.Blue, 'bold'),98    'numbers': format(DarkThemeColors.Teal),99}100 101 102class PythonHighlighter(QSyntaxHighlighter):103    """Syntax highlighter for the Python language.104    """105    # Python keywords106    keywords = [107        'and', 'assert', 'break', 'class', 'continue', 'def',108        'del', 'elif', 'else', 'except', 'exec', 'finally',109        'for', 'from', 'global', 'if', 'import', 'in',110        'is', 'lambda', 'not', 'or', 'pass', 'print',111        'raise', 'return', 'try', 'while', 'yield',112        'None', 'True', 'False', 'async', 'await',113    ]114 115    # Python operators116    operators = [117        r'=',118        # Comparison119        r'==', r'!=', r'<', r'<=', r'>', r'>=',120        # Arithmetic121        r'\+', r'-', r'\*', r'/', r'//', r'\%', r'\*\*',122        # In-place123        r'\+=', r'-=', r'\*=', r'/=', r'\%=',124        # Bitwise125        r'\^', r'\|', r'\&', r'\~', r'>>', r'<<',126    ]127 128    # Python braces129    braces = [130        r'\{', r'\}', r'\(', r'\)', r'\[', r'\]',131    ]132 133    def __init__(self, document):134        QSyntaxHighlighter.__init__(self, document)135 136        # Multi-line strings (expression, flag, style)137        # FIXME: The triple-quotes in these two lines will mess up the138        # syntax highlighting from this point onward139        self.tri_single = (QRegExp("'''"), 1, 'string2')140        self.tri_double = (QRegExp('"""'), 2, 'string2')141 142        rules = []143 144        # Keyword, operator, and brace rules145        rules += [(r'\b%s\b' % w, 0, 'keyword')146                  for w in PythonHighlighter.keywords]147        rules += [(r'%s' % o, 0, 'operator')148                  for o in PythonHighlighter.operators]149        rules += [(r'%s' % b, 0, 'brace')150                  for b in PythonHighlighter.braces]151 152        # All other rules153        rules += [154 155            # 'self'156            (r'\bself\b', 0, 'self'),157 158            # 'def' followed by an identifier159            (r'\bdef\b\s*(\w+)', 1, 'defclass'),160            # 'class' followed by an identifier161            (r'\bclass\b\s*(\w+)', 1, 'defclass'),162 163            # Numeric literals164            (r'\b[+-]?[0-9]+[lL]?\b', 0, 'numbers'),165            (r'\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b', 0, 'numbers'),166            (r'\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b', 0, 'numbers'),167 168            # Double-quoted string, possibly containing escape sequences169            (r'"[^"\\]*(\\.[^"\\]*)*"', 0, 'string'),170            # Single-quoted string, possibly containing escape sequences171            (r"'[^'\\]*(\\.[^'\\]*)*'", 0, 'string'),172 173            # From '#' until a newline174            (r'#[^\n]*', 0, 'comment'),175 176        ]177 178        # Build a QRegExp for each pattern179        self.rules = [(QRegExp(pat), index, fmt)180                      for (pat, index, fmt) in rules]181 182    @property183    def styles(self):184        app = QtWidgets.QApplication.instance()185        return DARK_STYLES if app.property('darkMode') else LIGHT_STYLES186 187    def highlightBlock(self, text):188        """Apply syntax highlighting to the given block of text."""189 190        rules = self.rules.copy()191        string_spans = []192 193        # First: apply string rules and record spans194        for expression, nth, format in rules:195            if format not in ('string', 'string2'):196                continue197            format = self.styles[format]198            for n, match in enumerate(re.finditer(expression, text)):199                if n < nth:200                    continue201                start, end = match.span()202                self.setFormat(start, end - start, format)203                string_spans.append((start, end))204 205        # Then: apply other rules only if not in a string206        for expression, nth, format in rules:207            if format in ('string', 'string2'):208                continue209            format = self.styles[format]210            for n, match in enumerate(re.finditer(expression, text)):211                if n < nth:212                    continue213                start, end = match.span()214                if any(start < e and end > s for s, e in string_spans):215                    continue  # Skip overlapping with string216                self.setFormat(start, end - start, format)217 218        self.applySearchHighlight(text)219        self.setCurrentBlockState(0)220 221        # Do multi-line strings222        in_multiline = self.match_multiline(text, *self.tri_single)223        if not in_multiline:224            in_multiline = self.match_multiline(text, *self.tri_double)225 226    def match_multiline(self, text, delimiter, in_state, style):227        """Do highlighting of multi-line strings. ``delimiter`` should be a228        ``QRegExp`` for triple-single-quotes or triple-double-quotes, and229        ``in_state`` should be a unique integer to represent the corresponding230        state changes when inside those strings. Returns True if we're still231        inside a multi-line string when this function is finished.232        """233        # If inside triple-single quotes, start at 0234        if self.previousBlockState() == in_state:235            start = 0236            add = 0237        # Otherwise, look for the delimiter on this line238        else:239            start = delimiter.indexIn(text)240            # Move past this match241            add = delimiter.matchedLength()242 243        # As long as there's a delimiter match on this line...244        while start >= 0:245            # Look for the ending delimiter246            end = delimiter.indexIn(text, start + add)247            # Ending delimiter on this line?248            if end >= add:249                length = end - start + add + delimiter.matchedLength()250                self.setCurrentBlockState(0)251            # No; multi-line string252            else:253                self.setCurrentBlockState(in_state)254                length = len(text) - start + add255            # Apply formatting256            self.setFormat(start, length, self.styles[style])257            # Look for the next match258            start = delimiter.indexIn(text, start + length)259 260        # Return True if still inside a multi-line string, False otherwise261        if self.currentBlockState() == in_state:262            return True263        else:264            return False265 
Aluode/PerceptionLabPortable · CoolFace