1# -*- coding: utf-8 -*-
2#
3# Copyright © Spyder Project Contributors
4# Licensed under the terms of the MIT License
5# (see spyder/__init__.py for details)
6
7"""
8Editor widget syntax highlighters based on QtGui.QSyntaxHighlighter
9(Python syntax highlighting rules are inspired from idlelib)
10"""
11
12# Standard library imports
13from __future__ import print_function
14import keyword
15import os
16import re
17
18# Third party imports
19from qtpy.QtCore import Qt
20from qtpy.QtGui import (QColor, QCursor, QFont, QSyntaxHighlighter,
21                        QTextCharFormat, QTextOption)
22from qtpy.QtWidgets import QApplication
23
24# Local imports
25from spyder import dependencies
26from spyder.config.base import _
27from spyder.config.main import CONF
28from spyder.py3compat import builtins, is_text_string, to_text_string
29from spyder.utils.sourcecode import CELL_LANGUAGES
30from spyder.utils.workers import WorkerManager
31
32
33PYGMENTS_REQVER = '>=2.0'
34dependencies.add("pygments", _("Syntax highlighting for Matlab, Julia and "
35                               "other file types"),
36                 required_version=PYGMENTS_REQVER)
37
38
39# =============================================================================
40# Constants
41# =============================================================================
42COLOR_SCHEME_KEYS = {
43                      "background":     _("Background:"),
44                      "currentline":    _("Current line:"),
45                      "currentcell":    _("Current cell:"),
46                      "occurrence":     _("Occurrence:"),
47                      "ctrlclick":      _("Link:"),
48                      "sideareas":      _("Side areas:"),
49                      "matched_p":      _("Matched <br>parens:"),
50                      "unmatched_p":    _("Unmatched <br>parens:"),
51                      "normal":         _("Normal text:"),
52                      "keyword":        _("Keyword:"),
53                      "builtin":        _("Builtin:"),
54                      "definition":     _("Definition:"),
55                      "comment":        _("Comment:"),
56                      "string":         _("String:"),
57                      "number":         _("Number:"),
58                      "instance":       _("Instance:"),
59                      }
60COLOR_SCHEME_NAMES = CONF.get('color_schemes', 'names')
61# Mapping for file extensions that use Pygments highlighting but should use
62# different lexers than Pygments' autodetection suggests.  Keys are file
63# extensions or tuples of extensions, values are Pygments lexer names.
64CUSTOM_EXTENSION_LEXER = {'.ipynb': 'json',
65                          '.txt': 'text',
66                          '.nt': 'bat',
67                          '.m': 'matlab',
68                          ('.properties', '.session', '.inf', '.reg', '.url',
69                           '.cfg', '.cnf', '.aut', '.iss'): 'ini'}
70# Convert custom extensions into a one-to-one mapping for easier lookup.
71custom_extension_lexer_mapping = {}
72for key, value in CUSTOM_EXTENSION_LEXER.items():
73    # Single key is mapped unchanged.
74    if is_text_string(key):
75        custom_extension_lexer_mapping[key] = value
76    # Tuple of keys is iterated over and each is mapped to value.
77    else:
78        for k in key:
79            custom_extension_lexer_mapping[k] = value
80
81
82#==============================================================================
83# Auxiliary functions
84#==============================================================================
85def get_color_scheme(name):
86    """Get a color scheme from config using its name"""
87    name = name.lower()
88    scheme = {}
89    for key in COLOR_SCHEME_KEYS:
90        try:
91            scheme[key] = CONF.get('color_schemes', name+'/'+key)
92        except:
93            scheme[key] = CONF.get('color_schemes', 'spyder/'+key)
94    return scheme
95
96
97#==============================================================================
98# Syntax highlighting color schemes
99#==============================================================================
100class BaseSH(QSyntaxHighlighter):
101    """Base Syntax Highlighter Class"""
102    # Syntax highlighting rules:
103    PROG = None
104    BLANKPROG = re.compile(r"\s+")
105    # Syntax highlighting states (from one text block to another):
106    NORMAL = 0
107    # Syntax highlighting parameters.
108    BLANK_ALPHA_FACTOR = 0.31
109
110    def __init__(self, parent, font=None, color_scheme='Spyder'):
111        QSyntaxHighlighter.__init__(self, parent)
112
113        self.outlineexplorer_data = {}
114
115        self.font = font
116        if is_text_string(color_scheme):
117            self.color_scheme = get_color_scheme(color_scheme)
118        else:
119            self.color_scheme = color_scheme
120
121        self.background_color = None
122        self.currentline_color = None
123        self.currentcell_color = None
124        self.occurrence_color = None
125        self.ctrlclick_color = None
126        self.sideareas_color = None
127        self.matched_p_color = None
128        self.unmatched_p_color = None
129
130        self.formats = None
131        self.setup_formats(font)
132
133        self.cell_separators = None
134
135    def get_background_color(self):
136        return QColor(self.background_color)
137
138    def get_foreground_color(self):
139        """Return foreground ('normal' text) color"""
140        return self.formats["normal"].foreground().color()
141
142    def get_currentline_color(self):
143        return QColor(self.currentline_color)
144
145    def get_currentcell_color(self):
146        return QColor(self.currentcell_color)
147
148    def get_occurrence_color(self):
149        return QColor(self.occurrence_color)
150
151    def get_ctrlclick_color(self):
152        return QColor(self.ctrlclick_color)
153
154    def get_sideareas_color(self):
155        return QColor(self.sideareas_color)
156
157    def get_matched_p_color(self):
158        return QColor(self.matched_p_color)
159
160    def get_unmatched_p_color(self):
161        return QColor(self.unmatched_p_color)
162
163    def get_comment_color(self):
164        """ Return color for the comments """
165        return self.formats['comment'].foreground().color()
166
167    def get_color_name(self, fmt):
168        """Return color name assigned to a given format"""
169        return self.formats[fmt].foreground().color().name()
170
171    def setup_formats(self, font=None):
172        base_format = QTextCharFormat()
173        if font is not None:
174            self.font = font
175        if self.font is not None:
176            base_format.setFont(self.font)
177        self.formats = {}
178        colors = self.color_scheme.copy()
179        self.background_color = colors.pop("background")
180        self.currentline_color = colors.pop("currentline")
181        self.currentcell_color = colors.pop("currentcell")
182        self.occurrence_color = colors.pop("occurrence")
183        self.ctrlclick_color = colors.pop("ctrlclick")
184        self.sideareas_color = colors.pop("sideareas")
185        self.matched_p_color = colors.pop("matched_p")
186        self.unmatched_p_color = colors.pop("unmatched_p")
187        for name, (color, bold, italic) in list(colors.items()):
188            format = QTextCharFormat(base_format)
189            format.setForeground(QColor(color))
190            format.setBackground(QColor(self.background_color))
191            if bold:
192                format.setFontWeight(QFont.Bold)
193            format.setFontItalic(italic)
194            self.formats[name] = format
195
196    def set_color_scheme(self, color_scheme):
197        if is_text_string(color_scheme):
198            self.color_scheme = get_color_scheme(color_scheme)
199        else:
200            self.color_scheme = color_scheme
201        self.setup_formats()
202        self.rehighlight()
203
204    def highlightBlock(self, text):
205        raise NotImplementedError
206
207    def highlight_spaces(self, text, offset=0):
208        """
209        Make blank space less apparent by setting the foreground alpha.
210        This only has an effect when 'Show blank space' is turned on.
211        Derived classes could call this function at the end of
212        highlightBlock().
213        """
214        flags_text = self.document().defaultTextOption().flags()
215        show_blanks =  flags_text & QTextOption.ShowTabsAndSpaces
216        if show_blanks:
217            format_leading = self.formats.get("leading", None)
218            format_trailing = self.formats.get("trailing", None)
219            match = self.BLANKPROG.search(text, offset)
220            while match:
221                start, end = match.span()
222                start = max([0, start+offset])
223                end = max([0, end+offset])
224                # Format trailing spaces at the end of the line.
225                if end == len(text) and format_trailing is not None:
226                    self.setFormat(start, end, format_trailing)
227                # Format leading spaces, e.g. indentation.
228                if start == 0 and format_leading is not None:
229                    self.setFormat(start, end, format_leading)
230                format = self.format(start)
231                color_foreground = format.foreground().color()
232                alpha_new = self.BLANK_ALPHA_FACTOR * color_foreground.alphaF()
233                color_foreground.setAlphaF(alpha_new)
234                self.setFormat(start, end-start, color_foreground)
235                match = self.BLANKPROG.search(text, match.end())
236
237    def get_outlineexplorer_data(self):
238        return self.outlineexplorer_data
239
240    def rehighlight(self):
241        self.outlineexplorer_data = {}
242        QApplication.setOverrideCursor(QCursor(Qt.WaitCursor))
243        QSyntaxHighlighter.rehighlight(self)
244        QApplication.restoreOverrideCursor()
245
246
247class TextSH(BaseSH):
248    """Simple Text Syntax Highlighter Class (only highlight spaces)"""
249    def highlightBlock(self, text):
250        self.highlight_spaces(text)
251
252
253class GenericSH(BaseSH):
254    """Generic Syntax Highlighter"""
255    # Syntax highlighting rules:
256    PROG = None  # to be redefined in child classes
257    def highlightBlock(self, text):
258        text = to_text_string(text)
259        self.setFormat(0, len(text), self.formats["normal"])
260
261        match = self.PROG.search(text)
262        index = 0
263        while match:
264            for key, value in list(match.groupdict().items()):
265                if value:
266                    start, end = match.span(key)
267                    index += end-start
268                    self.setFormat(start, end-start, self.formats[key])
269
270            match = self.PROG.search(text, match.end())
271
272        self.highlight_spaces(text)
273
274
275#==============================================================================
276# Python syntax highlighter
277#==============================================================================
278def any(name, alternates):
279    "Return a named group pattern matching list of alternates."
280    return "(?P<%s>" % name + "|".join(alternates) + ")"
281
282def make_python_patterns(additional_keywords=[], additional_builtins=[]):
283    "Strongly inspired from idlelib.ColorDelegator.make_pat"
284    kwlist = keyword.kwlist + additional_keywords
285    builtinlist = [str(name) for name in dir(builtins)
286                   if not name.startswith('_')] + additional_builtins
287    repeated = set(kwlist) & set(builtinlist)
288    for repeated_element in repeated:
289        kwlist.remove(repeated_element)
290    kw = r"\b" + any("keyword", kwlist) + r"\b"
291    builtin = r"([^.'\"\\#]\b|^)" + any("builtin", builtinlist) + r"\b"
292    comment = any("comment", [r"#[^\n]*"])
293    instance = any("instance", [r"\bself\b",
294                                (r"^\s*@([a-zA-Z_][a-zA-Z0-9_]*)"
295                                     r"(\.[a-zA-Z_][a-zA-Z0-9_]*)*")])
296    number = any("number",
297                 [r"\b[+-]?[0-9]+[lLjJ]?\b",
298                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
299                  r"\b[+-]?0[oO][0-7]+[lL]?\b",
300                  r"\b[+-]?0[bB][01]+[lL]?\b",
301                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?[jJ]?\b"])
302    sqstring =     r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
303    dqstring =     r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
304    uf_sqstring =  r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*(\\)$(?!')$"
305    uf_dqstring =  r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*(\\)$(?!")$'
306    sq3string =    r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(''')?"
307    dq3string =    r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(""")?'
308    uf_sq3string = r"(\b[rRuU])?'''[^'\\]*((\\.|'(?!''))[^'\\]*)*(\\)?(?!''')$"
309    uf_dq3string = r'(\b[rRuU])?"""[^"\\]*((\\.|"(?!""))[^"\\]*)*(\\)?(?!""")$'
310    string = any("string", [sq3string, dq3string, sqstring, dqstring])
311    ufstring1 = any("uf_sqstring", [uf_sqstring])
312    ufstring2 = any("uf_dqstring", [uf_dqstring])
313    ufstring3 = any("uf_sq3string", [uf_sq3string])
314    ufstring4 = any("uf_dq3string", [uf_dq3string])
315    return "|".join([instance, kw, builtin, comment,
316                     ufstring1, ufstring2, ufstring3, ufstring4, string,
317                     number, any("SYNC", [r"\n"])])
318
319class OutlineExplorerData(object):
320    CLASS, FUNCTION, STATEMENT, COMMENT, CELL = list(range(5))
321    FUNCTION_TOKEN = 'def'
322    CLASS_TOKEN = 'class'
323
324    def __init__(self):
325        self.text = None
326        self.fold_level = None
327        self.def_type = None
328        self.def_name = None
329
330    def is_not_class_nor_function(self):
331        return self.def_type not in (self.CLASS, self.FUNCTION)
332
333    def is_class_or_function(self):
334        return self.def_type in (self.CLASS, self.FUNCTION)
335
336    def is_comment(self):
337        return self.def_type in (self.COMMENT, self.CELL)
338
339    def get_class_name(self):
340        if self.def_type == self.CLASS:
341            return self.def_name
342
343    def get_function_name(self):
344        if self.def_type == self.FUNCTION:
345            return self.def_name
346
347    def get_token(self):
348        if self.def_type == self.FUNCTION:
349            token = self.FUNCTION_TOKEN
350        elif self.def_type == self.CLASS:
351            token = self.CLASS_TOKEN
352
353        return token
354
355
356class PythonSH(BaseSH):
357    """Python Syntax Highlighter"""
358    # Syntax highlighting rules:
359    add_kw = ['async', 'await']
360    PROG = re.compile(make_python_patterns(additional_keywords=add_kw), re.S)
361    IDPROG = re.compile(r"\s+(\w+)", re.S)
362    ASPROG = re.compile(r".*?\b(as)\b")
363    # Syntax highlighting states (from one text block to another):
364    (NORMAL, INSIDE_SQ3STRING, INSIDE_DQ3STRING,
365     INSIDE_SQSTRING, INSIDE_DQSTRING) = list(range(5))
366    DEF_TYPES = {"def": OutlineExplorerData.FUNCTION,
367                 "class": OutlineExplorerData.CLASS}
368    # Comments suitable for Outline Explorer
369    OECOMMENT = re.compile(r'^(# ?--[-]+|##[#]+ )[ -]*[^- ]+')
370
371    def __init__(self, parent, font=None, color_scheme='Spyder'):
372        BaseSH.__init__(self, parent, font, color_scheme)
373        self.import_statements = {}
374        self.found_cell_separators = False
375        self.cell_separators = CELL_LANGUAGES['Python']
376
377    def highlightBlock(self, text):
378        text = to_text_string(text)
379        prev_state = self.previousBlockState()
380        if prev_state == self.INSIDE_DQ3STRING:
381            offset = -4
382            text = r'""" '+text
383        elif prev_state == self.INSIDE_SQ3STRING:
384            offset = -4
385            text = r"''' "+text
386        elif prev_state == self.INSIDE_DQSTRING:
387            offset = -2
388            text = r'" '+text
389        elif prev_state == self.INSIDE_SQSTRING:
390            offset = -2
391            text = r"' "+text
392        else:
393            offset = 0
394            prev_state = self.NORMAL
395
396        oedata = None
397        import_stmt = None
398
399        self.setFormat(0, len(text), self.formats["normal"])
400
401        state = self.NORMAL
402        match = self.PROG.search(text)
403        while match:
404            for key, value in list(match.groupdict().items()):
405                if value:
406                    start, end = match.span(key)
407                    start = max([0, start+offset])
408                    end = max([0, end+offset])
409                    if key == "uf_sq3string":
410                        self.setFormat(start, end-start,
411                                       self.formats["string"])
412                        state = self.INSIDE_SQ3STRING
413                    elif key == "uf_dq3string":
414                        self.setFormat(start, end-start,
415                                       self.formats["string"])
416                        state = self.INSIDE_DQ3STRING
417                    elif key == "uf_sqstring":
418                        self.setFormat(start, end-start,
419                                       self.formats["string"])
420                        state = self.INSIDE_SQSTRING
421                    elif key == "uf_dqstring":
422                        self.setFormat(start, end-start,
423                                       self.formats["string"])
424                        state = self.INSIDE_DQSTRING
425                    else:
426                        self.setFormat(start, end-start, self.formats[key])
427                        if key == "comment":
428                            if text.lstrip().startswith(self.cell_separators):
429                                self.found_cell_separators = True
430                                oedata = OutlineExplorerData()
431                                oedata.text = to_text_string(text).strip()
432                                oedata.fold_level = start
433                                oedata.def_type = OutlineExplorerData.CELL
434                                oedata.def_name = text.strip()
435                            elif self.OECOMMENT.match(text.lstrip()):
436                                oedata = OutlineExplorerData()
437                                oedata.text = to_text_string(text).strip()
438                                oedata.fold_level = start
439                                oedata.def_type = OutlineExplorerData.COMMENT
440                                oedata.def_name = text.strip()
441                        elif key == "keyword":
442                            if value in ("def", "class"):
443                                match1 = self.IDPROG.match(text, end)
444                                if match1:
445                                    start1, end1 = match1.span(1)
446                                    self.setFormat(start1, end1-start1,
447                                                   self.formats["definition"])
448                                    oedata = OutlineExplorerData()
449                                    oedata.text = to_text_string(text)
450                                    oedata.fold_level = start
451                                    oedata.def_type = self.DEF_TYPES[
452                                                        to_text_string(value)]
453                                    oedata.def_name = text[start1:end1]
454                                    oedata.color = self.formats["definition"]
455                            elif value in ("elif", "else", "except", "finally",
456                                           "for", "if", "try", "while",
457                                           "with"):
458                                if text.lstrip().startswith(value):
459                                    oedata = OutlineExplorerData()
460                                    oedata.text = to_text_string(text).strip()
461                                    oedata.fold_level = start
462                                    oedata.def_type = \
463                                        OutlineExplorerData.STATEMENT
464                                    oedata.def_name = text.strip()
465                            elif value == "import":
466                                import_stmt = text.strip()
467                                # color all the "as" words on same line, except
468                                # if in a comment; cheap approximation to the
469                                # truth
470                                if '#' in text:
471                                    endpos = text.index('#')
472                                else:
473                                    endpos = len(text)
474                                while True:
475                                    match1 = self.ASPROG.match(text, end,
476                                                               endpos)
477                                    if not match1:
478                                        break
479                                    start, end = match1.span(1)
480                                    self.setFormat(start, end-start,
481                                                   self.formats["keyword"])
482
483            match = self.PROG.search(text, match.end())
484
485        self.setCurrentBlockState(state)
486
487        # Use normal format for indentation and trailing spaces.
488        self.formats['leading'] = self.formats['normal']
489        self.formats['trailing'] = self.formats['normal']
490        self.highlight_spaces(text, offset)
491
492        if oedata is not None:
493            block_nb = self.currentBlock().blockNumber()
494            self.outlineexplorer_data[block_nb] = oedata
495            self.outlineexplorer_data['found_cell_separators'] = self.found_cell_separators
496        if import_stmt is not None:
497            block_nb = self.currentBlock().blockNumber()
498            self.import_statements[block_nb] = import_stmt
499
500    def get_import_statements(self):
501        return list(self.import_statements.values())
502
503    def rehighlight(self):
504        self.import_statements = {}
505        self.found_cell_separators = False
506        BaseSH.rehighlight(self)
507
508
509#==============================================================================
510# Cython syntax highlighter
511#==============================================================================
512C_TYPES = 'bool char double enum float int long mutable short signed struct unsigned void'
513
514class CythonSH(PythonSH):
515    """Cython Syntax Highlighter"""
516    ADDITIONAL_KEYWORDS = [
517        "cdef", "ctypedef", "cpdef", "inline", "cimport", "extern",
518        "include", "begin", "end", "by", "gil", "nogil", "const", "public",
519        "readonly", "fused", "static", "api", "DEF", "IF", "ELIF", "ELSE"]
520
521    ADDITIONAL_BUILTINS = C_TYPES.split() + [
522        "array", "bint", "Py_ssize_t", "intern", "reload", "sizeof", "NULL"]
523    PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS,
524                                           ADDITIONAL_BUILTINS), re.S)
525    IDPROG = re.compile(r"\s+([\w\.]+)", re.S)
526
527
528#==============================================================================
529# Enaml syntax highlighter
530#==============================================================================
531class EnamlSH(PythonSH):
532    """Enaml Syntax Highlighter"""
533    ADDITIONAL_KEYWORDS = ["enamldef", "template", "attr", "event", "const", "alias",
534                           "func"]
535    ADDITIONAL_BUILTINS = []
536    PROG = re.compile(make_python_patterns(ADDITIONAL_KEYWORDS,
537                                           ADDITIONAL_BUILTINS), re.S)
538    IDPROG = re.compile(r"\s+([\w\.]+)", re.S)
539
540
541#==============================================================================
542# C/C++ syntax highlighter
543#==============================================================================
544C_KEYWORDS1 = 'and and_eq bitand bitor break case catch const const_cast continue default delete do dynamic_cast else explicit export extern for friend goto if inline namespace new not not_eq operator or or_eq private protected public register reinterpret_cast return sizeof static static_cast switch template throw try typedef typeid typename union using virtual while xor xor_eq'
545C_KEYWORDS2 = 'a addindex addtogroup anchor arg attention author b brief bug c class code date def defgroup deprecated dontinclude e em endcode endhtmlonly ifdef endif endlatexonly endlink endverbatim enum example exception f$ file fn hideinitializer htmlinclude htmlonly if image include ingroup internal invariant interface latexonly li line link mainpage name namespace nosubgrouping note overload p page par param post pre ref relates remarks return retval sa section see showinitializer since skip skipline subsection test throw todo typedef union until var verbatim verbinclude version warning weakgroup'
546C_KEYWORDS3 = 'asm auto class compl false true volatile wchar_t'
547
548def make_generic_c_patterns(keywords, builtins,
549                            instance=None, define=None, comment=None):
550    "Strongly inspired from idlelib.ColorDelegator.make_pat"
551    kw = r"\b" + any("keyword", keywords.split()) + r"\b"
552    builtin = r"\b" + any("builtin", builtins.split()+C_TYPES.split()) + r"\b"
553    if comment is None:
554        comment = any("comment", [r"//[^\n]*", r"\/\*(.*?)\*\/"])
555    comment_start = any("comment_start", [r"\/\*"])
556    comment_end = any("comment_end", [r"\*\/"])
557    if instance is None:
558        instance = any("instance", [r"\bthis\b"])
559    number = any("number",
560                 [r"\b[+-]?[0-9]+[lL]?\b",
561                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
562                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"])
563    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
564    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
565    string = any("string", [sqstring, dqstring])
566    if define is None:
567        define = any("define", [r"#[^\n]*"])
568    return "|".join([instance, kw, comment, string, number,
569                     comment_start, comment_end, builtin,
570                     define, any("SYNC", [r"\n"])])
571
572def make_cpp_patterns():
573    return make_generic_c_patterns(C_KEYWORDS1+' '+C_KEYWORDS2, C_KEYWORDS3)
574
575class CppSH(BaseSH):
576    """C/C++ Syntax Highlighter"""
577    # Syntax highlighting rules:
578    PROG = re.compile(make_cpp_patterns(), re.S)
579    # Syntax highlighting states (from one text block to another):
580    NORMAL = 0
581    INSIDE_COMMENT = 1
582    def __init__(self, parent, font=None, color_scheme=None):
583        BaseSH.__init__(self, parent, font, color_scheme)
584
585    def highlightBlock(self, text):
586        text = to_text_string(text)
587        inside_comment = self.previousBlockState() == self.INSIDE_COMMENT
588        self.setFormat(0, len(text),
589                       self.formats["comment" if inside_comment else "normal"])
590
591        match = self.PROG.search(text)
592        index = 0
593        while match:
594            for key, value in list(match.groupdict().items()):
595                if value:
596                    start, end = match.span(key)
597                    index += end-start
598                    if key == "comment_start":
599                        inside_comment = True
600                        self.setFormat(start, len(text)-start,
601                                       self.formats["comment"])
602                    elif key == "comment_end":
603                        inside_comment = False
604                        self.setFormat(start, end-start,
605                                       self.formats["comment"])
606                    elif inside_comment:
607                        self.setFormat(start, end-start,
608                                       self.formats["comment"])
609                    elif key == "define":
610                        self.setFormat(start, end-start,
611                                       self.formats["number"])
612                    else:
613                        self.setFormat(start, end-start, self.formats[key])
614
615            match = self.PROG.search(text, match.end())
616
617        self.highlight_spaces(text)
618
619        last_state = self.INSIDE_COMMENT if inside_comment else self.NORMAL
620        self.setCurrentBlockState(last_state)
621
622
623def make_opencl_patterns():
624    # Keywords:
625    kwstr1 = 'cl_char cl_uchar cl_short cl_ushort cl_int cl_uint cl_long cl_ulong cl_half cl_float cl_double cl_platform_id cl_device_id cl_context cl_command_queue cl_mem cl_program cl_kernel cl_event cl_sampler cl_bool cl_bitfield cl_device_type cl_platform_info cl_device_info cl_device_address_info cl_device_fp_config cl_device_mem_cache_type cl_device_local_mem_type cl_device_exec_capabilities cl_command_queue_properties cl_context_properties cl_context_info cl_command_queue_info cl_channel_order cl_channel_type cl_mem_flags cl_mem_object_type cl_mem_info cl_image_info cl_addressing_mode cl_filter_mode cl_sampler_info cl_map_flags cl_program_info cl_program_build_info cl_build_status cl_kernel_info cl_kernel_work_group_info cl_event_info cl_command_type cl_profiling_info cl_image_format'
626    # Constants:
627    kwstr2 = 'CL_FALSE, CL_TRUE, CL_PLATFORM_PROFILE, CL_PLATFORM_VERSION, CL_PLATFORM_NAME, CL_PLATFORM_VENDOR, CL_PLATFORM_EXTENSIONS, CL_DEVICE_TYPE_DEFAULT , CL_DEVICE_TYPE_CPU, CL_DEVICE_TYPE_GPU, CL_DEVICE_TYPE_ACCELERATOR, CL_DEVICE_TYPE_ALL, CL_DEVICE_TYPE, CL_DEVICE_VENDOR_ID, CL_DEVICE_MAX_COMPUTE_UNITS, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, CL_DEVICE_MAX_WORK_GROUP_SIZE, CL_DEVICE_MAX_WORK_ITEM_SIZES, CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR, CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG, CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT, CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE, CL_DEVICE_MAX_CLOCK_FREQUENCY, CL_DEVICE_ADDRESS_BITS, CL_DEVICE_MAX_READ_IMAGE_ARGS, CL_DEVICE_MAX_WRITE_IMAGE_ARGS, CL_DEVICE_MAX_MEM_ALLOC_SIZE, CL_DEVICE_IMAGE2D_MAX_WIDTH, CL_DEVICE_IMAGE2D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_WIDTH, CL_DEVICE_IMAGE3D_MAX_HEIGHT, CL_DEVICE_IMAGE3D_MAX_DEPTH, CL_DEVICE_IMAGE_SUPPORT, CL_DEVICE_MAX_PARAMETER_SIZE, CL_DEVICE_MAX_SAMPLERS, CL_DEVICE_MEM_BASE_ADDR_ALIGN, CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE, CL_DEVICE_SINGLE_FP_CONFIG, CL_DEVICE_GLOBAL_MEM_CACHE_TYPE, CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE, CL_DEVICE_GLOBAL_MEM_CACHE_SIZE, CL_DEVICE_GLOBAL_MEM_SIZE, CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE, CL_DEVICE_MAX_CONSTANT_ARGS, CL_DEVICE_LOCAL_MEM_TYPE, CL_DEVICE_LOCAL_MEM_SIZE, CL_DEVICE_ERROR_CORRECTION_SUPPORT, CL_DEVICE_PROFILING_TIMER_RESOLUTION, CL_DEVICE_ENDIAN_LITTLE, CL_DEVICE_AVAILABLE, CL_DEVICE_COMPILER_AVAILABLE, CL_DEVICE_EXECUTION_CAPABILITIES, CL_DEVICE_QUEUE_PROPERTIES, CL_DEVICE_NAME, CL_DEVICE_VENDOR, CL_DRIVER_VERSION, CL_DEVICE_PROFILE, CL_DEVICE_VERSION, CL_DEVICE_EXTENSIONS, CL_DEVICE_PLATFORM, CL_FP_DENORM, CL_FP_INF_NAN, CL_FP_ROUND_TO_NEAREST, CL_FP_ROUND_TO_ZERO, CL_FP_ROUND_TO_INF, CL_FP_FMA, CL_NONE, CL_READ_ONLY_CACHE, CL_READ_WRITE_CACHE, CL_LOCAL, CL_GLOBAL, CL_EXEC_KERNEL, CL_EXEC_NATIVE_KERNEL, CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE, CL_QUEUE_PROFILING_ENABLE, CL_CONTEXT_REFERENCE_COUNT, CL_CONTEXT_DEVICES, CL_CONTEXT_PROPERTIES, CL_CONTEXT_PLATFORM, CL_QUEUE_CONTEXT, CL_QUEUE_DEVICE, CL_QUEUE_REFERENCE_COUNT, CL_QUEUE_PROPERTIES, CL_MEM_READ_WRITE, CL_MEM_WRITE_ONLY, CL_MEM_READ_ONLY, CL_MEM_USE_HOST_PTR, CL_MEM_ALLOC_HOST_PTR, CL_MEM_COPY_HOST_PTR, CL_R, CL_A, CL_RG, CL_RA, CL_RGB, CL_RGBA, CL_BGRA, CL_ARGB, CL_INTENSITY, CL_LUMINANCE, CL_SNORM_INT8, CL_SNORM_INT16, CL_UNORM_INT8, CL_UNORM_INT16, CL_UNORM_SHORT_565, CL_UNORM_SHORT_555, CL_UNORM_INT_101010, CL_SIGNED_INT8, CL_SIGNED_INT16, CL_SIGNED_INT32, CL_UNSIGNED_INT8, CL_UNSIGNED_INT16, CL_UNSIGNED_INT32, CL_HALF_FLOAT, CL_FLOAT, CL_MEM_OBJECT_BUFFER, CL_MEM_OBJECT_IMAGE2D, CL_MEM_OBJECT_IMAGE3D, CL_MEM_TYPE, CL_MEM_FLAGS, CL_MEM_SIZECL_MEM_HOST_PTR, CL_MEM_HOST_PTR, CL_MEM_MAP_COUNT, CL_MEM_REFERENCE_COUNT, CL_MEM_CONTEXT, CL_IMAGE_FORMAT, CL_IMAGE_ELEMENT_SIZE, CL_IMAGE_ROW_PITCH, CL_IMAGE_SLICE_PITCH, CL_IMAGE_WIDTH, CL_IMAGE_HEIGHT, CL_IMAGE_DEPTH, CL_ADDRESS_NONE, CL_ADDRESS_CLAMP_TO_EDGE, CL_ADDRESS_CLAMP, CL_ADDRESS_REPEAT, CL_FILTER_NEAREST, CL_FILTER_LINEAR, CL_SAMPLER_REFERENCE_COUNT, CL_SAMPLER_CONTEXT, CL_SAMPLER_NORMALIZED_COORDS, CL_SAMPLER_ADDRESSING_MODE, CL_SAMPLER_FILTER_MODE, CL_MAP_READ, CL_MAP_WRITE, CL_PROGRAM_REFERENCE_COUNT, CL_PROGRAM_CONTEXT, CL_PROGRAM_NUM_DEVICES, CL_PROGRAM_DEVICES, CL_PROGRAM_SOURCE, CL_PROGRAM_BINARY_SIZES, CL_PROGRAM_BINARIES, CL_PROGRAM_BUILD_STATUS, CL_PROGRAM_BUILD_OPTIONS, CL_PROGRAM_BUILD_LOG, CL_BUILD_SUCCESS, CL_BUILD_NONE, CL_BUILD_ERROR, CL_BUILD_IN_PROGRESS, CL_KERNEL_FUNCTION_NAME, CL_KERNEL_NUM_ARGS, CL_KERNEL_REFERENCE_COUNT, CL_KERNEL_CONTEXT, CL_KERNEL_PROGRAM, CL_KERNEL_WORK_GROUP_SIZE, CL_KERNEL_COMPILE_WORK_GROUP_SIZE, CL_KERNEL_LOCAL_MEM_SIZE, CL_EVENT_COMMAND_QUEUE, CL_EVENT_COMMAND_TYPE, CL_EVENT_REFERENCE_COUNT, CL_EVENT_COMMAND_EXECUTION_STATUS, CL_COMMAND_NDRANGE_KERNEL, CL_COMMAND_TASK, CL_COMMAND_NATIVE_KERNEL, CL_COMMAND_READ_BUFFER, CL_COMMAND_WRITE_BUFFER, CL_COMMAND_COPY_BUFFER, CL_COMMAND_READ_IMAGE, CL_COMMAND_WRITE_IMAGE, CL_COMMAND_COPY_IMAGE, CL_COMMAND_COPY_IMAGE_TO_BUFFER, CL_COMMAND_COPY_BUFFER_TO_IMAGE, CL_COMMAND_MAP_BUFFER, CL_COMMAND_MAP_IMAGE, CL_COMMAND_UNMAP_MEM_OBJECT, CL_COMMAND_MARKER, CL_COMMAND_ACQUIRE_GL_OBJECTS, CL_COMMAND_RELEASE_GL_OBJECTS, command execution status, CL_COMPLETE, CL_RUNNING, CL_SUBMITTED, CL_QUEUED, CL_PROFILING_COMMAND_QUEUED, CL_PROFILING_COMMAND_SUBMIT, CL_PROFILING_COMMAND_START, CL_PROFILING_COMMAND_END, CL_CHAR_BIT, CL_SCHAR_MAX, CL_SCHAR_MIN, CL_CHAR_MAX, CL_CHAR_MIN, CL_UCHAR_MAX, CL_SHRT_MAX, CL_SHRT_MIN, CL_USHRT_MAX, CL_INT_MAX, CL_INT_MIN, CL_UINT_MAX, CL_LONG_MAX, CL_LONG_MIN, CL_ULONG_MAX, CL_FLT_DIG, CL_FLT_MANT_DIG, CL_FLT_MAX_10_EXP, CL_FLT_MAX_EXP, CL_FLT_MIN_10_EXP, CL_FLT_MIN_EXP, CL_FLT_RADIX, CL_FLT_MAX, CL_FLT_MIN, CL_FLT_EPSILON, CL_DBL_DIG, CL_DBL_MANT_DIG, CL_DBL_MAX_10_EXP, CL_DBL_MAX_EXP, CL_DBL_MIN_10_EXP, CL_DBL_MIN_EXP, CL_DBL_RADIX, CL_DBL_MAX, CL_DBL_MIN, CL_DBL_EPSILON, CL_SUCCESS, CL_DEVICE_NOT_FOUND, CL_DEVICE_NOT_AVAILABLE, CL_COMPILER_NOT_AVAILABLE, CL_MEM_OBJECT_ALLOCATION_FAILURE, CL_OUT_OF_RESOURCES, CL_OUT_OF_HOST_MEMORY, CL_PROFILING_INFO_NOT_AVAILABLE, CL_MEM_COPY_OVERLAP, CL_IMAGE_FORMAT_MISMATCH, CL_IMAGE_FORMAT_NOT_SUPPORTED, CL_BUILD_PROGRAM_FAILURE, CL_MAP_FAILURE, CL_INVALID_VALUE, CL_INVALID_DEVICE_TYPE, CL_INVALID_PLATFORM, CL_INVALID_DEVICE, CL_INVALID_CONTEXT, CL_INVALID_QUEUE_PROPERTIES, CL_INVALID_COMMAND_QUEUE, CL_INVALID_HOST_PTR, CL_INVALID_MEM_OBJECT, CL_INVALID_IMAGE_FORMAT_DESCRIPTOR, CL_INVALID_IMAGE_SIZE, CL_INVALID_SAMPLER, CL_INVALID_BINARY, CL_INVALID_BUILD_OPTIONS, CL_INVALID_PROGRAM, CL_INVALID_PROGRAM_EXECUTABLE, CL_INVALID_KERNEL_NAME, CL_INVALID_KERNEL_DEFINITION, CL_INVALID_KERNEL, CL_INVALID_ARG_INDEX, CL_INVALID_ARG_VALUE, CL_INVALID_ARG_SIZE, CL_INVALID_KERNEL_ARGS, CL_INVALID_WORK_DIMENSION, CL_INVALID_WORK_GROUP_SIZE, CL_INVALID_WORK_ITEM_SIZE, CL_INVALID_GLOBAL_OFFSET, CL_INVALID_EVENT_WAIT_LIST, CL_INVALID_EVENT, CL_INVALID_OPERATION, CL_INVALID_GL_OBJECT, CL_INVALID_BUFFER_SIZE, CL_INVALID_MIP_LEVEL, CL_INVALID_GLOBAL_WORK_SIZE'
628    # Functions:
629    builtins = 'clGetPlatformIDs, clGetPlatformInfo, clGetDeviceIDs, clGetDeviceInfo, clCreateContext, clCreateContextFromType, clReleaseContext, clGetContextInfo, clCreateCommandQueue, clRetainCommandQueue, clReleaseCommandQueue, clGetCommandQueueInfo, clSetCommandQueueProperty, clCreateBuffer, clCreateImage2D, clCreateImage3D, clRetainMemObject, clReleaseMemObject, clGetSupportedImageFormats, clGetMemObjectInfo, clGetImageInfo, clCreateSampler, clRetainSampler, clReleaseSampler, clGetSamplerInfo, clCreateProgramWithSource, clCreateProgramWithBinary, clRetainProgram, clReleaseProgram, clBuildProgram, clUnloadCompiler, clGetProgramInfo, clGetProgramBuildInfo, clCreateKernel, clCreateKernelsInProgram, clRetainKernel, clReleaseKernel, clSetKernelArg, clGetKernelInfo, clGetKernelWorkGroupInfo, clWaitForEvents, clGetEventInfo, clRetainEvent, clReleaseEvent, clGetEventProfilingInfo, clFlush, clFinish, clEnqueueReadBuffer, clEnqueueWriteBuffer, clEnqueueCopyBuffer, clEnqueueReadImage, clEnqueueWriteImage, clEnqueueCopyImage, clEnqueueCopyImageToBuffer, clEnqueueCopyBufferToImage, clEnqueueMapBuffer, clEnqueueMapImage, clEnqueueUnmapMemObject, clEnqueueNDRangeKernel, clEnqueueTask, clEnqueueNativeKernel, clEnqueueMarker, clEnqueueWaitForEvents, clEnqueueBarrier'
630    # Qualifiers:
631    qualifiers = '__global __local __constant __private __kernel'
632    keyword_list = C_KEYWORDS1+' '+C_KEYWORDS2+' '+kwstr1+' '+kwstr2
633    builtin_list = C_KEYWORDS3+' '+builtins+' '+qualifiers
634    return make_generic_c_patterns(keyword_list, builtin_list)
635
636class OpenCLSH(CppSH):
637    """OpenCL Syntax Highlighter"""
638    PROG = re.compile(make_opencl_patterns(), re.S)
639
640
641#==============================================================================
642# Fortran Syntax Highlighter
643#==============================================================================
644
645def make_fortran_patterns():
646    "Strongly inspired from idlelib.ColorDelegator.make_pat"
647    kwstr = 'access action advance allocatable allocate apostrophe assign assignment associate asynchronous backspace bind blank blockdata call case character class close common complex contains continue cycle data deallocate decimal delim default dimension direct do dowhile double doubleprecision else elseif elsewhere encoding end endassociate endblockdata enddo endfile endforall endfunction endif endinterface endmodule endprogram endselect endsubroutine endtype endwhere entry eor equivalence err errmsg exist exit external file flush fmt forall form format formatted function go goto id if implicit in include inout integer inquire intent interface intrinsic iomsg iolength iostat kind len logical module name named namelist nextrec nml none nullify number only open opened operator optional out pad parameter pass pause pending pointer pos position precision print private program protected public quote read readwrite real rec recl recursive result return rewind save select selectcase selecttype sequential sign size stat status stop stream subroutine target then to type unformatted unit use value volatile wait where while write'
648    bistr1 = 'abs achar acos acosd adjustl adjustr aimag aimax0 aimin0 aint ajmax0 ajmin0 akmax0 akmin0 all allocated alog alog10 amax0 amax1 amin0 amin1 amod anint any asin asind associated atan atan2 atan2d atand bitest bitl bitlr bitrl bjtest bit_size bktest break btest cabs ccos cdabs cdcos cdexp cdlog cdsin cdsqrt ceiling cexp char clog cmplx conjg cos cosd cosh count cpu_time cshift csin csqrt dabs dacos dacosd dasin dasind datan datan2 datan2d datand date date_and_time dble dcmplx dconjg dcos dcosd dcosh dcotan ddim dexp dfloat dflotk dfloti dflotj digits dim dimag dint dlog dlog10 dmax1 dmin1 dmod dnint dot_product dprod dreal dsign dsin dsind dsinh dsqrt dtan dtand dtanh eoshift epsilon errsns exp exponent float floati floatj floatk floor fraction free huge iabs iachar iand ibclr ibits ibset ichar idate idim idint idnint ieor ifix iiabs iiand iibclr iibits iibset iidim iidint iidnnt iieor iifix iint iior iiqint iiqnnt iishft iishftc iisign ilen imax0 imax1 imin0 imin1 imod index inint inot int int1 int2 int4 int8 iqint iqnint ior ishft ishftc isign isnan izext jiand jibclr jibits jibset jidim jidint jidnnt jieor jifix jint jior jiqint jiqnnt jishft jishftc jisign jmax0 jmax1 jmin0 jmin1 jmod jnint jnot jzext kiabs kiand kibclr kibits kibset kidim kidint kidnnt kieor kifix kind kint kior kishft kishftc kisign kmax0 kmax1 kmin0 kmin1 kmod knint knot kzext lbound leadz len len_trim lenlge lge lgt lle llt log log10 logical lshift malloc matmul max max0 max1 maxexponent maxloc maxval merge min min0 min1 minexponent minloc minval mod modulo mvbits nearest nint not nworkers number_of_processors pack popcnt poppar precision present product radix random random_number random_seed range real repeat reshape rrspacing rshift scale scan secnds selected_int_kind selected_real_kind set_exponent shape sign sin sind sinh size sizeof sngl snglq spacing spread sqrt sum system_clock tan tand tanh tiny transfer transpose trim ubound unpack verify'
649    bistr2 = 'cdabs cdcos cdexp cdlog cdsin cdsqrt cotan cotand dcmplx dconjg dcotan dcotand decode dimag dll_export dll_import doublecomplex dreal dvchk encode find flen flush getarg getcharqq getcl getdat getenv gettim hfix ibchng identifier imag int1 int2 int4 intc intrup invalop iostat_msg isha ishc ishl jfix lacfar locking locnear map nargs nbreak ndperr ndpexc offset ovefl peekcharqq precfill prompt qabs qacos qacosd qasin qasind qatan qatand qatan2 qcmplx qconjg qcos qcosd qcosh qdim qexp qext qextd qfloat qimag qlog qlog10 qmax1 qmin1 qmod qreal qsign qsin qsind qsinh qsqrt qtan qtand qtanh ran rand randu rewrite segment setdat settim system timer undfl unlock union val virtual volatile zabs zcos zexp zlog zsin zsqrt'
650    kw = r"\b" + any("keyword", kwstr.split()) + r"\b"
651    builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b"
652    comment = any("comment", [r"\![^\n]*"])
653    number = any("number",
654                 [r"\b[+-]?[0-9]+[lL]?\b",
655                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
656                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"])
657    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
658    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
659    string = any("string", [sqstring, dqstring])
660    return "|".join([kw, comment, string, number, builtin,
661                     any("SYNC", [r"\n"])])
662
663class FortranSH(BaseSH):
664    """Fortran Syntax Highlighter"""
665    # Syntax highlighting rules:
666    PROG = re.compile(make_fortran_patterns(), re.S|re.I)
667    IDPROG = re.compile(r"\s+(\w+)", re.S)
668    # Syntax highlighting states (from one text block to another):
669    NORMAL = 0
670    def __init__(self, parent, font=None, color_scheme=None):
671        BaseSH.__init__(self, parent, font, color_scheme)
672
673    def highlightBlock(self, text):
674        text = to_text_string(text)
675        self.setFormat(0, len(text), self.formats["normal"])
676
677        match = self.PROG.search(text)
678        index = 0
679        while match:
680            for key, value in list(match.groupdict().items()):
681                if value:
682                    start, end = match.span(key)
683                    index += end-start
684                    self.setFormat(start, end-start, self.formats[key])
685                    if value.lower() in ("subroutine", "module", "function"):
686                        match1 = self.IDPROG.match(text, end)
687                        if match1:
688                            start1, end1 = match1.span(1)
689                            self.setFormat(start1, end1-start1,
690                                           self.formats["definition"])
691
692            match = self.PROG.search(text, match.end())
693
694        self.highlight_spaces(text)
695
696class Fortran77SH(FortranSH):
697    """Fortran 77 Syntax Highlighter"""
698    def highlightBlock(self, text):
699        text = to_text_string(text)
700        if text.startswith(("c", "C")):
701            self.setFormat(0, len(text), self.formats["comment"])
702            self.highlight_spaces(text)
703        else:
704            FortranSH.highlightBlock(self, text)
705            self.setFormat(0, 5, self.formats["comment"])
706            self.setFormat(73, max([73, len(text)]),
707                           self.formats["comment"])
708
709
710#==============================================================================
711# IDL highlighter
712#
713# Contribution from Stuart Mumford (Littlemumford) - 02/02/2012
714# See Issue #850
715#==============================================================================
716def make_idl_patterns():
717    "Strongly inspired from idlelib.ColorDelegator.make_pat"
718    kwstr = 'begin of pro function endfor endif endwhile endrep endcase endswitch end if then else for do while repeat until break case switch common continue exit return goto help message print read retall stop'
719    bistr1 = 'a_correlate abs acos adapt_hist_equal alog alog10 amoeba arg_present arra_equal array_indices ascii_template asin assoc atan beseli beselj besel k besely beta bilinear bin_date binary_template dinfgen dinomial blk_con broyden bytarr byte bytscl c_correlate call_external call_function ceil chebyshev check_math chisqr_cvf chisqr_pdf choldc cholsol cindgen clust_wts cluster color_quan colormap_applicable comfit complex complexarr complexround compute_mesh_normals cond congrid conj convert_coord convol coord2to3 correlate cos cosh cramer create_struct crossp crvlength ct_luminance cti_test curvefit cv_coord cvttobm cw_animate cw_arcball cw_bgroup cw_clr_index cw_colorsel cw_defroi cw_field cw_filesel cw_form cw_fslider cw_light_editor cw_orient cw_palette_editor cw_pdmenu cw_rgbslider cw_tmpl cw_zoom dblarr dcindgen dcomplexarr defroi deriv derivsig determ diag_matrix dialog_message dialog_pickfile pialog_printersetup dialog_printjob dialog_read_image dialog_write_image digital_filter dilate dindgen dist double eigenql eigenvec elmhes eof erode erf erfc erfcx execute exp expand_path expint extrac extract_slice f_cvf f_pdf factorial fft file_basename file_dirname file_expand_path file_info file_same file_search file_test file_which filepath findfile findgen finite fix float floor fltarr format_axis_values fstat fulstr fv_test fx_root fz_roots gamma gauss_cvf gauss_pdf gauss2dfit gaussfit gaussint get_drive_list get_kbrd get_screen_size getenv grid_tps grid3 griddata gs_iter hanning hdf_browser hdf_read hilbert hist_2d hist_equal histogram hough hqr ibeta identity idl_validname idlitsys_createtool igamma imaginary indgen int_2d int_3d int_tabulated intarr interpol interpolate invert ioctl ishft julday keword_set krig2d kurtosis kw_test l64indgen label_date label_region ladfit laguerre la_cholmprove la_cholsol la_Determ la_eigenproblem la_eigenql la_eigenvec la_elmhes la_gm_linear_model la_hqr la_invert la_least_square_equality la_least_squares la_linear_equation la_lumprove la_lusol la_trimprove la_trisol leefit legendre linbcg lindgen linfit ll_arc_distance lmfit lmgr lngamma lnp_test locale_get logical_and logical_or logical_true lon64arr lonarr long long64 lsode lu_complex lumprove lusol m_correlate machar make_array map_2points map_image map_patch map_proj_forward map_proj_init map_proj_inverse matrix_multiply matrix_power max md_test mean meanabsdev median memory mesh_clip mesh_decimate mesh_issolid mesh_merge mesh_numtriangles mesh_smooth mesh_surfacearea mesh_validate mesh_volume min min_curve_surf moment morph_close morph_distance morph_gradient morph_histormiss morph_open morph_thin morph_tophat mpeg_open msg_cat_open n_elements n_params n_tags newton norm obj_class obj_isa obj_new obj_valid objarr p_correlate path_sep pcomp pnt_line polar_surface poly poly_2d poly_area poly_fit polyfillv ployshade primes product profile profiles project_vol ptr_new ptr_valid ptrarr qgrid3 qromb qromo qsimp query_bmp query_dicom query_image query_jpeg query_mrsid query_pict query_png query_ppm query_srf query_tiff query_wav r_correlate r_test radon randomn randomu ranks read_ascii read_binary read_bmp read_dicom read_image read_mrsid read_png read_spr read_sylk read_tiff read_wav read_xwd real_part rebin recall_commands recon3 reform region_grow regress replicate reverse rk4 roberts rot rotate round routine_info rs_test s_test savgol search2d search3d sfit shift shmdebug shmvar simplex sin sindgen sinh size skewness smooth sobel sort sph_scat spher_harm spl_init spl_interp spline spline_p sprsab sprsax sprsin sprstp sqrt standardize stddev strarr strcmp strcompress stregex string strjoin strlen strlowcase strmatch strmessage strmid strpos strsplit strtrim strupcase svdfit svsol swap_endian systime t_cvf t_pdf tag_names tan tanh temporary tetra_clip tetra_surface tetra_volume thin timegen tm_test total trace transpose tri_surf trigrid trisol ts_coef ts_diff ts_fcast ts_smooth tvrd uindgen unit uintarr ul64indgen ulindgen ulon64arr ulonarr ulong ulong64 uniq value_locate variance vert_t3d voigt voxel_proj warp_tri watershed where widget_actevix widget_base widget_button widget_combobox widget_draw widget_droplist widget_event widget_info widget_label widget_list widget_propertsheet widget_slider widget_tab widget_table widget_text widget_tree write_sylk wtn xfont xregistered xsq_test'
720    bistr2 = 'annotate arrow axis bar_plot blas_axpy box_cursor breakpoint byteorder caldata calendar call_method call_procedure catch cd cir_3pnt close color_convert compile_opt constrained_min contour copy_lun cpu create_view cursor cw_animate_getp cw_animate_load cw_animate_run cw_light_editor_get cw_light_editor_set cw_palette_editor_get cw_palette_editor_set define_key define_msgblk define_msgblk_from_file defsysv delvar device dfpmin dissolve dlm_load doc_librar draw_roi efont empty enable_sysrtn erase errplot expand file_chmod file_copy file_delete file_lines file_link file_mkdir file_move file_readlink flick flow3 flush forward_function free_lun funct gamma_ct get_lun grid_input h_eq_ct h_eq_int heap_free heap_gc hls hsv icontour iimage image_cont image_statistics internal_volume iplot isocontour isosurface isurface itcurrent itdelete itgetcurrent itregister itreset ivolume journal la_choldc la_ludc la_svd la_tridc la_triql la_trired linkimage loadct ludc make_dll map_continents map_grid map_proj_info map_set mesh_obj mk_html_help modifyct mpeg_close mpeg_put mpeg_save msg_cat_close msg_cat_compile multi obj_destroy on_error on_ioerror online_help openr openw openu oplot oploterr particle_trace path_cache plot plot_3dbox plot_field ploterr plots point_lun polar_contour polyfill polywarp popd powell printf printd ps_show_fonts psafm pseudo ptr_free pushd qhull rdpix readf read_interfile read_jpeg read_pict read_ppm read_srf read_wave read_x11_bitmap reads readu reduce_colors register_cursor replicate_inplace resolve_all resolve_routine restore save scale3 scale3d set_plot set_shading setenv setup_keys shade_surf shade_surf_irr shade_volume shmmap show3 showfont skip_lun slicer3 slide_image socket spawn sph_4pnt streamline stretch strput struct_assign struct_hide surface surfr svdc swap_enian_inplace t3d tek_color threed time_test2 triangulate triql trired truncate_lun tv tvcrs tvlct tvscl usersym vector_field vel velovect voronoi wait wdelete wf_draw widget_control widget_displaycontextmenu window write_bmp write_image write_jpeg write_nrif write_pict write_png write_ppm write_spr write_srf write_tiff write_wav write_wave writeu wset wshow xbm_edit xdisplayfile xdxf xinteranimate xloadct xmanager xmng_tmpl xmtool xobjview xobjview_rotate xobjview_write_image xpalette xpcolo xplot3d xroi xsurface xvaredit xvolume xyouts zoom zoom_24'
721    kw = r"\b" + any("keyword", kwstr.split()) + r"\b"
722    builtin = r"\b" + any("builtin", bistr1.split()+bistr2.split()) + r"\b"
723    comment = any("comment", [r"\;[^\n]*"])
724    number = any("number",
725                 [r"\b[+-]?[0-9]+[lL]?\b",
726                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
727		  r"\b\.[0-9]d0|\.d0+[lL]?\b",
728                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"])
729    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
730    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
731    string = any("string", [sqstring, dqstring])
732    return "|".join([kw, comment, string, number, builtin,
733                     any("SYNC", [r"\n"])])
734
735class IdlSH(GenericSH):
736    """IDL Syntax Highlighter"""
737    PROG = re.compile(make_idl_patterns(), re.S|re.I)
738
739
740#==============================================================================
741# Diff/Patch highlighter
742#==============================================================================
743
744class DiffSH(BaseSH):
745    """Simple Diff/Patch Syntax Highlighter Class"""
746    def highlightBlock(self, text):
747        text = to_text_string(text)
748        if text.startswith("+++"):
749            self.setFormat(0, len(text), self.formats["keyword"])
750        elif text.startswith("---"):
751            self.setFormat(0, len(text), self.formats["keyword"])
752        elif text.startswith("+"):
753            self.setFormat(0, len(text), self.formats["string"])
754        elif text.startswith("-"):
755            self.setFormat(0, len(text), self.formats["number"])
756        elif text.startswith("@"):
757            self.setFormat(0, len(text), self.formats["builtin"])
758
759        self.highlight_spaces(text)
760
761#==============================================================================
762# NSIS highlighter
763#==============================================================================
764
765def make_nsis_patterns():
766    "Strongly inspired from idlelib.ColorDelegator.make_pat"
767    kwstr1 = 'Abort AddBrandingImage AddSize AllowRootDirInstall AllowSkipFiles AutoCloseWindow BGFont BGGradient BrandingText BringToFront Call CallInstDLL Caption ClearErrors CompletedText ComponentText CopyFiles CRCCheck CreateDirectory CreateFont CreateShortCut Delete DeleteINISec DeleteINIStr DeleteRegKey DeleteRegValue DetailPrint DetailsButtonText DirText DirVar DirVerify EnableWindow EnumRegKey EnumRegValue Exec ExecShell ExecWait Exch ExpandEnvStrings File FileBufSize FileClose FileErrorText FileOpen FileRead FileReadByte FileSeek FileWrite FileWriteByte FindClose FindFirst FindNext FindWindow FlushINI Function FunctionEnd GetCurInstType GetCurrentAddress GetDlgItem GetDLLVersion GetDLLVersionLocal GetErrorLevel GetFileTime GetFileTimeLocal GetFullPathName GetFunctionAddress GetInstDirError GetLabelAddress GetTempFileName Goto HideWindow ChangeUI CheckBitmap Icon IfAbort IfErrors IfFileExists IfRebootFlag IfSilent InitPluginsDir InstallButtonText InstallColors InstallDir InstallDirRegKey InstProgressFlags InstType InstTypeGetText InstTypeSetText IntCmp IntCmpU IntFmt IntOp IsWindow LangString LicenseBkColor LicenseData LicenseForceSelection LicenseLangString LicenseText LoadLanguageFile LogSet LogText MessageBox MiscButtonText Name OutFile Page PageCallbacks PageEx PageExEnd Pop Push Quit ReadEnvStr ReadINIStr ReadRegDWORD ReadRegStr Reboot RegDLL Rename ReserveFile Return RMDir SearchPath Section SectionEnd SectionGetFlags SectionGetInstTypes SectionGetSize SectionGetText SectionIn SectionSetFlags SectionSetInstTypes SectionSetSize SectionSetText SendMessage SetAutoClose SetBrandingImage SetCompress SetCompressor SetCompressorDictSize SetCtlColors SetCurInstType SetDatablockOptimize SetDateSave SetDetailsPrint SetDetailsView SetErrorLevel SetErrors SetFileAttributes SetFont SetOutPath SetOverwrite SetPluginUnload SetRebootFlag SetShellVarContext SetSilent ShowInstDetails ShowUninstDetails ShowWindow SilentInstall SilentUnInstall Sleep SpaceTexts StrCmp StrCpy StrLen SubCaption SubSection SubSectionEnd UninstallButtonText UninstallCaption UninstallIcon UninstallSubCaption UninstallText UninstPage UnRegDLL Var VIAddVersionKey VIProductVersion WindowIcon WriteINIStr WriteRegBin WriteRegDWORD WriteRegExpandStr WriteRegStr WriteUninstaller XPStyle'
768    kwstr2 = 'all alwaysoff ARCHIVE auto both bzip2 components current custom details directory false FILE_ATTRIBUTE_ARCHIVE FILE_ATTRIBUTE_HIDDEN FILE_ATTRIBUTE_NORMAL FILE_ATTRIBUTE_OFFLINE FILE_ATTRIBUTE_READONLY FILE_ATTRIBUTE_SYSTEM FILE_ATTRIBUTE_TEMPORARY force grey HIDDEN hide IDABORT IDCANCEL IDIGNORE IDNO IDOK IDRETRY IDYES ifdiff ifnewer instfiles instfiles lastused leave left level license listonly lzma manual MB_ABORTRETRYIGNORE MB_DEFBUTTON1 MB_DEFBUTTON2 MB_DEFBUTTON3 MB_DEFBUTTON4 MB_ICONEXCLAMATION MB_ICONINFORMATION MB_ICONQUESTION MB_ICONSTOP MB_OK MB_OKCANCEL MB_RETRYCANCEL MB_RIGHT MB_SETFOREGROUND MB_TOPMOST MB_YESNO MB_YESNOCANCEL nevershow none NORMAL off OFFLINE on READONLY right RO show silent silentlog SYSTEM TEMPORARY text textonly true try uninstConfirm windows zlib'
769    kwstr3 = 'MUI_ABORTWARNING MUI_ABORTWARNING_CANCEL_DEFAULT MUI_ABORTWARNING_TEXT MUI_BGCOLOR MUI_COMPONENTSPAGE_CHECKBITMAP MUI_COMPONENTSPAGE_NODESC MUI_COMPONENTSPAGE_SMALLDESC MUI_COMPONENTSPAGE_TEXT_COMPLIST MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_INFO MUI_COMPONENTSPAGE_TEXT_DESCRIPTION_TITLE MUI_COMPONENTSPAGE_TEXT_INSTTYPE MUI_COMPONENTSPAGE_TEXT_TOP MUI_CUSTOMFUNCTION_ABORT MUI_CUSTOMFUNCTION_GUIINIT MUI_CUSTOMFUNCTION_UNABORT MUI_CUSTOMFUNCTION_UNGUIINIT MUI_DESCRIPTION_TEXT MUI_DIRECTORYPAGE_BGCOLOR MUI_DIRECTORYPAGE_TEXT_DESTINATION MUI_DIRECTORYPAGE_TEXT_TOP MUI_DIRECTORYPAGE_VARIABLE MUI_DIRECTORYPAGE_VERIFYONLEAVE MUI_FINISHPAGE_BUTTON MUI_FINISHPAGE_CANCEL_ENABLED MUI_FINISHPAGE_LINK MUI_FINISHPAGE_LINK_COLOR MUI_FINISHPAGE_LINK_LOCATION MUI_FINISHPAGE_NOAUTOCLOSE MUI_FINISHPAGE_NOREBOOTSUPPORT MUI_FINISHPAGE_REBOOTLATER_DEFAULT MUI_FINISHPAGE_RUN MUI_FINISHPAGE_RUN_FUNCTION MUI_FINISHPAGE_RUN_NOTCHECKED MUI_FINISHPAGE_RUN_PARAMETERS MUI_FINISHPAGE_RUN_TEXT MUI_FINISHPAGE_SHOWREADME MUI_FINISHPAGE_SHOWREADME_FUNCTION MUI_FINISHPAGE_SHOWREADME_NOTCHECKED MUI_FINISHPAGE_SHOWREADME_TEXT MUI_FINISHPAGE_TEXT MUI_FINISHPAGE_TEXT_LARGE MUI_FINISHPAGE_TEXT_REBOOT MUI_FINISHPAGE_TEXT_REBOOTLATER MUI_FINISHPAGE_TEXT_REBOOTNOW MUI_FINISHPAGE_TITLE MUI_FINISHPAGE_TITLE_3LINES MUI_FUNCTION_DESCRIPTION_BEGIN MUI_FUNCTION_DESCRIPTION_END MUI_HEADER_TEXT MUI_HEADER_TRANSPARENT_TEXT MUI_HEADERIMAGE MUI_HEADERIMAGE_BITMAP MUI_HEADERIMAGE_BITMAP_NOSTRETCH MUI_HEADERIMAGE_BITMAP_RTL MUI_HEADERIMAGE_BITMAP_RTL_NOSTRETCH MUI_HEADERIMAGE_RIGHT MUI_HEADERIMAGE_UNBITMAP MUI_HEADERIMAGE_UNBITMAP_NOSTRETCH MUI_HEADERIMAGE_UNBITMAP_RTL MUI_HEADERIMAGE_UNBITMAP_RTL_NOSTRETCH MUI_HWND MUI_ICON MUI_INSTALLCOLORS MUI_INSTALLOPTIONS_DISPLAY MUI_INSTALLOPTIONS_DISPLAY_RETURN MUI_INSTALLOPTIONS_EXTRACT MUI_INSTALLOPTIONS_EXTRACT_AS MUI_INSTALLOPTIONS_INITDIALOG MUI_INSTALLOPTIONS_READ MUI_INSTALLOPTIONS_SHOW MUI_INSTALLOPTIONS_SHOW_RETURN MUI_INSTALLOPTIONS_WRITE MUI_INSTFILESPAGE_ABORTHEADER_SUBTEXT MUI_INSTFILESPAGE_ABORTHEADER_TEXT MUI_INSTFILESPAGE_COLORS MUI_INSTFILESPAGE_FINISHHEADER_SUBTEXT MUI_INSTFILESPAGE_FINISHHEADER_TEXT MUI_INSTFILESPAGE_PROGRESSBAR MUI_LANGDLL_ALLLANGUAGES MUI_LANGDLL_ALWAYSSHOW MUI_LANGDLL_DISPLAY MUI_LANGDLL_INFO MUI_LANGDLL_REGISTRY_KEY MUI_LANGDLL_REGISTRY_ROOT MUI_LANGDLL_REGISTRY_VALUENAME MUI_LANGDLL_WINDOWTITLE MUI_LANGUAGE MUI_LICENSEPAGE_BGCOLOR MUI_LICENSEPAGE_BUTTON MUI_LICENSEPAGE_CHECKBOX MUI_LICENSEPAGE_CHECKBOX_TEXT MUI_LICENSEPAGE_RADIOBUTTONS MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_ACCEPT MUI_LICENSEPAGE_RADIOBUTTONS_TEXT_DECLINE MUI_LICENSEPAGE_TEXT_BOTTOM MUI_LICENSEPAGE_TEXT_TOP MUI_PAGE_COMPONENTS MUI_PAGE_CUSTOMFUNCTION_LEAVE MUI_PAGE_CUSTOMFUNCTION_PRE MUI_PAGE_CUSTOMFUNCTION_SHOW MUI_PAGE_DIRECTORY MUI_PAGE_FINISH MUI_PAGE_HEADER_SUBTEXT MUI_PAGE_HEADER_TEXT MUI_PAGE_INSTFILES MUI_PAGE_LICENSE MUI_PAGE_STARTMENU MUI_PAGE_WELCOME MUI_RESERVEFILE_INSTALLOPTIONS MUI_RESERVEFILE_LANGDLL MUI_SPECIALINI MUI_STARTMENU_GETFOLDER MUI_STARTMENU_WRITE_BEGIN MUI_STARTMENU_WRITE_END MUI_STARTMENUPAGE_BGCOLOR MUI_STARTMENUPAGE_DEFAULTFOLDER MUI_STARTMENUPAGE_NODISABLE MUI_STARTMENUPAGE_REGISTRY_KEY MUI_STARTMENUPAGE_REGISTRY_ROOT MUI_STARTMENUPAGE_REGISTRY_VALUENAME MUI_STARTMENUPAGE_TEXT_CHECKBOX MUI_STARTMENUPAGE_TEXT_TOP MUI_UI MUI_UI_COMPONENTSPAGE_NODESC MUI_UI_COMPONENTSPAGE_SMALLDESC MUI_UI_HEADERIMAGE MUI_UI_HEADERIMAGE_RIGHT MUI_UNABORTWARNING MUI_UNABORTWARNING_CANCEL_DEFAULT MUI_UNABORTWARNING_TEXT MUI_UNCONFIRMPAGE_TEXT_LOCATION MUI_UNCONFIRMPAGE_TEXT_TOP MUI_UNFINISHPAGE_NOAUTOCLOSE MUI_UNFUNCTION_DESCRIPTION_BEGIN MUI_UNFUNCTION_DESCRIPTION_END MUI_UNGETLANGUAGE MUI_UNICON MUI_UNPAGE_COMPONENTS MUI_UNPAGE_CONFIRM MUI_UNPAGE_DIRECTORY MUI_UNPAGE_FINISH MUI_UNPAGE_INSTFILES MUI_UNPAGE_LICENSE MUI_UNPAGE_WELCOME MUI_UNWELCOMEFINISHPAGE_BITMAP MUI_UNWELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_UNWELCOMEFINISHPAGE_INI MUI_WELCOMEFINISHPAGE_BITMAP MUI_WELCOMEFINISHPAGE_BITMAP_NOSTRETCH MUI_WELCOMEFINISHPAGE_CUSTOMFUNCTION_INIT MUI_WELCOMEFINISHPAGE_INI MUI_WELCOMEPAGE_TEXT MUI_WELCOMEPAGE_TITLE MUI_WELCOMEPAGE_TITLE_3LINES'
770    bistr = 'addincludedir addplugindir AndIf cd define echo else endif error execute If ifdef ifmacrodef ifmacrondef ifndef include insertmacro macro macroend onGUIEnd onGUIInit onInit onInstFailed onInstSuccess onMouseOverSection onRebootFailed onSelChange onUserAbort onVerifyInstDir OrIf packhdr system undef verbose warning'
771    instance = any("instance", [r'\$\{.*?\}', r'\$[A-Za-z0-9\_]*'])
772    define = any("define", [r"\![^\n]*"])
773    comment = any("comment", [r"\;[^\n]*", r"\#[^\n]*", r"\/\*(.*?)\*\/"])
774    return make_generic_c_patterns(kwstr1+' '+kwstr2+' '+kwstr3, bistr,
775                                   instance=instance, define=define,
776                                   comment=comment)
777
778class NsisSH(CppSH):
779    """NSIS Syntax Highlighter"""
780    # Syntax highlighting rules:
781    PROG = re.compile(make_nsis_patterns(), re.S)
782
783
784#==============================================================================
785# gettext highlighter
786#==============================================================================
787
788def make_gettext_patterns():
789    "Strongly inspired from idlelib.ColorDelegator.make_pat"
790    kwstr = 'msgid msgstr'
791    kw = r"\b" + any("keyword", kwstr.split()) + r"\b"
792    fuzzy = any("builtin", [r"#,[^\n]*"])
793    links = any("normal", [r"#:[^\n]*"])
794    comment = any("comment", [r"#[^\n]*"])
795    number = any("number",
796                 [r"\b[+-]?[0-9]+[lL]?\b",
797                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
798                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"])
799    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
800    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
801    string = any("string", [sqstring, dqstring])
802    return "|".join([kw, string, number, fuzzy, links, comment,
803                     any("SYNC", [r"\n"])])
804
805class GetTextSH(GenericSH):
806    """gettext Syntax Highlighter"""
807    # Syntax highlighting rules:
808    PROG = re.compile(make_gettext_patterns(), re.S)
809
810#==============================================================================
811# yaml highlighter
812#==============================================================================
813
814def make_yaml_patterns():
815    "Strongly inspired from sublime highlighter "
816    kw = any("keyword", [r":|>|-|\||\[|\]|[A-Za-z][\w\s\-\_ ]+(?=:)"])
817    links = any("normal", [r"#:[^\n]*"])
818    comment = any("comment", [r"#[^\n]*"])
819    number = any("number",
820                 [r"\b[+-]?[0-9]+[lL]?\b",
821                  r"\b[+-]?0[xX][0-9A-Fa-f]+[lL]?\b",
822                  r"\b[+-]?[0-9]+(?:\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\b"])
823    sqstring = r"(\b[rRuU])?'[^'\\\n]*(\\.[^'\\\n]*)*'?"
824    dqstring = r'(\b[rRuU])?"[^"\\\n]*(\\.[^"\\\n]*)*"?'
825    string = any("string", [sqstring, dqstring])
826    return "|".join([kw, string, number, links, comment,
827                     any("SYNC", [r"\n"])])
828
829class YamlSH(GenericSH):
830    """yaml Syntax Highlighter"""
831    # Syntax highlighting rules:
832    PROG = re.compile(make_yaml_patterns(), re.S)
833
834
835#==============================================================================
836# HTML highlighter
837#==============================================================================
838
839class BaseWebSH(BaseSH):
840    """Base class for CSS and HTML syntax highlighters"""
841    NORMAL  = 0
842    COMMENT = 1
843
844    def __init__(self, parent, font=None, color_scheme=None):
845        BaseSH.__init__(self, parent, font, color_scheme)
846
847    def highlightBlock(self, text):
848        text = to_text_string(text)
849        previous_state = self.previousBlockState()
850
851        if previous_state == self.COMMENT:
852            self.setFormat(0, len(text), self.formats["comment"])
853        else:
854            previous_state = self.NORMAL
855            self.setFormat(0, len(text), self.formats["normal"])
856
857        self.setCurrentBlockState(previous_state)
858        match = self.PROG.search(text)
859
860        match_count = 0
861        n_characters = len(text)
862        # There should never be more matches than characters in the text.
863        while match and match_count < n_characters:
864            match_dict = match.groupdict()
865            for key, value in list(match_dict.items()):
866                if value:
867                    start, end = match.span(key)
868                    if previous_state == self.COMMENT:
869                        if key == "multiline_comment_end":
870                            self.setCurrentBlockState(self.NORMAL)
871                            self.setFormat(end, len(text),
872                                           self.formats["normal"])
873                        else:
874                            self.setCurrentBlockState(self.COMMENT)
875                            self.setFormat(0, len(text),
876                                           self.formats["comment"])
877                    else:
878                        if key == "multiline_comment_start":
879                            self.setCurrentBlockState(self.COMMENT)
880                            self.setFormat(start, len(text),
881                                           self.formats["comment"])
882                        else:
883                            self.setCurrentBlockState(self.NORMAL)
884                            try:
885                                self.setFormat(start, end-start,
886                                               self.formats[key])
887                            except KeyError:
888                                # happens with unmatched end-of-comment;
889                                # see issue 1462
890                                pass
891
892            match = self.PROG.search(text, match.end())
893            match_count += 1
894
895        self.highlight_spaces(text)
896
897def make_html_patterns():
898    """Strongly inspired from idlelib.ColorDelegator.make_pat """
899    tags = any("builtin", [r"<", r"[\?/]?>", r"(?<=<).*?(?=[ >])"])
900    keywords = any("keyword", [r" [\w:-]*?(?==)"])
901    string = any("string", [r'".*?"'])
902    comment = any("comment", [r"<!--.*?-->"])
903    multiline_comment_start = any("multiline_comment_start", [r"<!--"])
904    multiline_comment_end = any("multiline_comment_end", [r"-->"])
905    return "|".join([comment, multiline_comment_start,
906                     multiline_comment_end, tags, keywords, string])
907
908class HtmlSH(BaseWebSH):
909    """HTML Syntax Highlighter"""
910    PROG = re.compile(make_html_patterns(), re.S)
911
912
913# =============================================================================
914# Markdown highlighter
915# =============================================================================
916
917def make_md_patterns():
918    h1 = '^#[^#]+'
919    h2 = '^##[^#]+'
920    h3 = '^###[^#]+'
921    h4 = '^####[^#]+'
922    h5 = '^#####[^#]+'
923    h6 = '^######[^#]+'
924
925    titles = any('title', [h1, h2, h3, h4, h5, h6])
926
927    html_tags = any("builtin", [r"<", r"[\?/]?>", r"(?<=<).*?(?=[ >])"])
928    html_symbols = '&[^; ].+;'
929    html_comment = '<!--.+-->'
930
931    strikethrough = any('strikethrough', [r'(~~)(.*?)~~'])
932    strong = any('strong', [r'(\*\*)(.*?)\*\*'])
933
934    italic = r'(__)(.*?)__'
935    emphasis = r'(//)(.*?)//'
936    italic = any('italic', [italic, emphasis])
937
938    # links - (links) after [] or links after []:
939    link_html = (r'(?<=(\]\())[^\(\)]*(?=\))|'
940                 '(<https?://[^>]+>)|'
941                 '(<[^ >]+@[^ >]+>)')
942    # link/image references - [] or ![]
943    link = r'!?\[[^\[\]]*\]'
944    links = any('link', [link_html, link])
945
946    # blockquotes and lists -  > or - or * or 0.
947    blockquotes = (r'(^>+.*)'
948                   r'|(^(?:    |\t)*[0-9]+\. )'
949                   r'|(^(?:    |\t)*- )'
950                   r'|(^(?:    |\t)*\* )')
951    # code
952    code = any('code', ['^`{3,}.*$'])
953    inline_code = any('inline_code', ['`[^`]*`'])
954
955    # math - $$
956    math = any('number', [r'^(?:\${2}).*$', html_symbols])
957
958    comment = any('comment', [blockquotes, html_comment])
959
960    return '|'.join([titles, comment, html_tags, math, links, italic, strong,
961                     strikethrough, code, inline_code])
962
963
964class MarkdownSH(BaseSH):
965    """Markdown Syntax Highlighter"""
966    # Syntax highlighting rules:
967    PROG = re.compile(make_md_patterns(), re.S)
968    NORMAL = 0
969    CODE = 1
970
971    def highlightBlock(self, text):
972        text = to_text_string(text)
973        previous_state = self.previousBlockState()
974
975        if previous_state == self.CODE:
976            self.setFormat(0, len(text), self.formats["code"])
977        else:
978            previous_state = self.NORMAL
979            self.setFormat(0, len(text), self.formats["normal"])
980
981        self.setCurrentBlockState(previous_state)
982
983        match = self.PROG.search(text)
984        match_count = 0
985        n_characters = len(text)
986
987        while match and match_count< n_characters:
988            for key, value in list(match.groupdict().items()):
989                start, end = match.span(key)
990
991                if value:
992                    previous_state = self.previousBlockState()
993
994                    if previous_state == self.CODE:
995                        if key == "code":
996                            # Change to normal
997                            self.setFormat(0, len(text),
998                                           self.formats["normal"])
999                            self.setCurrentBlockState(self.NORMAL)
1000                        else:
1001                            continue
1002                    else:
1003                        if key == "code":
1004                            # Change to code
1005                            self.setFormat(0, len(text), self.formats["code"])
1006                            self.setCurrentBlockState(self.CODE)
1007                            continue
1008
1009                    self.setFormat(start, end - start, self.formats[key])
1010
1011            match = self.PROG.search(text, match.end())
1012            match_count += 1
1013
1014        self.highlight_spaces(text)
1015
1016    def setup_formats(self, font=None):
1017        super(MarkdownSH, self).setup_formats(font)
1018
1019        font = QTextCharFormat(self.formats['normal'])
1020        font.setFontItalic(True)
1021        self.formats['italic'] = font
1022
1023        self.formats['strong'] = self.formats['definition']
1024
1025        font = QTextCharFormat(self.formats['normal'])
1026        font.setFontStrikeOut(True)
1027        self.formats['strikethrough'] = font
1028
1029        font = QTextCharFormat(self.formats['string'])
1030        font.setUnderlineStyle(True)
1031        self.formats['link'] = font
1032
1033        self.formats['code'] = self.formats['string']
1034        self.formats['inline_code'] = self.formats['string']
1035
1036        font = QTextCharFormat(self.formats['keyword'])
1037        font.setFontWeight(QFont.Bold)
1038        self.formats['title'] = font
1039
1040
1041#==============================================================================
1042# Pygments based omni-parser
1043#==============================================================================
1044
1045# IMPORTANT NOTE:
1046# --------------
1047# Do not be tempted to generalize the use of PygmentsSH (that is tempting
1048# because it would lead to more generic and compact code, and not only in
1049# this very module) because this generic syntax highlighter is far slower
1050# than the native ones (all classes above). For example, a Python syntax
1051# highlighter based on PygmentsSH would be 2 to 3 times slower than the
1052# current native PythonSH syntax highlighter.
1053
1054class PygmentsSH(BaseSH):
1055    """ Generic Pygments syntax highlighter """
1056    # Store the language name and a ref to the lexer
1057    _lang_name = None
1058    _lexer = None
1059
1060    # Syntax highlighting states (from one text block to another):
1061    NORMAL = 0
1062    def __init__(self, parent, font=None, color_scheme=None):
1063        # Warning: do not move out those import statements
1064        # (pygments is an optional dependency)
1065        from pygments.lexers import get_lexer_by_name
1066        from pygments.token import (Text, Other, Keyword, Name, String, Number,
1067                                    Comment, Generic, Token)
1068        # Map Pygments tokens to Spyder tokens
1069        self._tokmap = {Text: "normal",
1070                        Generic: "normal",
1071                        Other: "normal",
1072                        Keyword: "keyword",
1073                        Token.Operator: "normal",
1074                        Name.Builtin: "builtin",
1075                        Name: "normal",
1076                        Comment: "comment",
1077                        String: "string",
1078                        Number: "number"}
1079        # Load Pygments' Lexer
1080        if self._lang_name is not None:
1081            self._lexer = get_lexer_by_name(self._lang_name)
1082
1083        BaseSH.__init__(self, parent, font, color_scheme)
1084
1085        # This worker runs in a thread to avoid blocking when doing full file
1086        # parsing
1087        self._worker_manager = WorkerManager()
1088
1089        # Store the format for all the tokens after Pygments parsing
1090        self._charlist = []
1091
1092        # Flag variable to avoid unnecessary highlights if the worker has not
1093        # yet finished processing
1094        self._allow_highlight = True
1095
1096    def make_charlist(self):
1097        """Parses the complete text and stores format for each character."""
1098
1099        def worker_output(worker, output, error):
1100            """Worker finished callback."""
1101            self._charlist = output
1102            if error is None and output:
1103                self._allow_highlight = True
1104                self.rehighlight()
1105            self._allow_highlight = False
1106
1107        text = to_text_string(self.document().toPlainText())
1108        tokens = self._lexer.get_tokens(text)
1109
1110        # Before starting a new worker process make sure to end previous
1111        # incarnations
1112        self._worker_manager.terminate_all()
1113
1114        worker = self._worker_manager.create_python_worker(
1115            self._make_charlist,
1116            tokens,
1117            self._tokmap,
1118            self.formats,
1119        )
1120        worker.sig_finished.connect(worker_output)
1121        worker.start()
1122
1123    def _make_charlist(self, tokens, tokmap, formats):
1124        """
1125        Parses the complete text and stores format for each character.
1126
1127        Uses the attached lexer to parse into a list of tokens and Pygments
1128        token types.  Then breaks tokens into individual letters, each with a
1129        Spyder token type attached.  Stores this list as self._charlist.
1130
1131        It's attached to the contentsChange signal of the parent QTextDocument
1132        so that the charlist is updated whenever the document changes.
1133        """
1134
1135        def _get_fmt(typ):
1136            """Get the Spyder format code for the given Pygments token type."""
1137            # Exact matches first
1138            if typ in tokmap:
1139                return tokmap[typ]
1140            # Partial (parent-> child) matches
1141            for key, val in tokmap.items():
1142                if typ in key: # Checks if typ is a subtype of key.
1143                    return val
1144
1145            return 'normal'
1146
1147        charlist = []
1148        for typ, token in tokens:
1149            fmt = formats[_get_fmt(typ)]
1150            for letter in token:
1151                charlist.append((fmt, letter))
1152
1153        return charlist
1154
1155    def highlightBlock(self, text):
1156        """ Actually highlight the block"""
1157        # Note that an undefined blockstate is equal to -1, so the first block
1158        # will have the correct behaviour of starting at 0.
1159        if self._allow_highlight:
1160            start = self.previousBlockState() + 1
1161            end = start + len(text)
1162            for i, (fmt, letter) in enumerate(self._charlist[start:end]):
1163                self.setFormat(i, 1, fmt)
1164            self.setCurrentBlockState(end)
1165            self.highlight_spaces(text)
1166
1167
1168def guess_pygments_highlighter(filename):
1169    """Factory to generate syntax highlighter for the given filename.
1170
1171    If a syntax highlighter is not available for a particular file, this
1172    function will attempt to generate one based on the lexers in Pygments.  If
1173    Pygments is not available or does not have an appropriate lexer, TextSH
1174    will be returned instead.
1175
1176    """
1177    try:
1178        from pygments.lexers import get_lexer_for_filename, get_lexer_by_name
1179        from pygments.util import ClassNotFound
1180    except ImportError:
1181        return TextSH
1182    root, ext = os.path.splitext(filename)
1183    if ext in custom_extension_lexer_mapping:
1184        lexer = get_lexer_by_name(custom_extension_lexer_mapping[ext])
1185    else:
1186        try:
1187            lexer = get_lexer_for_filename(filename)
1188        except ClassNotFound:
1189            return TextSH
1190    class GuessedPygmentsSH(PygmentsSH):
1191        _lexer = lexer
1192    return GuessedPygmentsSH
1193