1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114
|
#include <Python.h>
#include "script/syntax.h"
#include "app/colors.h"
ScriptHighlighter::ScriptHighlighter(QTextDocument* doc)
: QSyntaxHighlighter(doc)
{
PyObject* kwmod = PyImport_ImportModule("keyword");
PyObject* kwlist = PyObject_GetAttrString(kwmod, "kwlist");
QList<QString> keywords = {"input", "output", "title", "meta"};
// Get all of Python's keywords and add them to a list.
for (int i=0; i < PyList_Size(kwlist); ++i)
{
PyObject* kw = PyList_GetItem(kwlist, i);
wchar_t* w = PyUnicode_AsWideCharString(kw, NULL);
keywords << QString::fromWCharArray(w);
PyMem_Free(w);
}
Py_DECREF(kwlist);
Py_DECREF(kwmod);
// Make rules for all the Python keywords.
QTextCharFormat kw_format;
kw_format.setForeground(Colors::green);
for (auto k : keywords)
rules << Rule("\\b" + k + "\\b", kw_format);
QTextCharFormat quote_format;
quote_format.setForeground(Colors::brown);
// Triple-quoted (multiline) strings
// Single-line triple-quoted string
rules << Rule("'''.*?'''", quote_format);
rules << Rule("\"\"\".*?\"\"\"", quote_format);
// Beginning of multiline string
rules << Rule("'''.*$", quote_format, BASE, MULTILINE_SINGLE);
rules << Rule("\"\"\".*$", quote_format, BASE, MULTILINE_DOUBLE);
// End of multiline string
rules << Rule("^.*'''", quote_format, MULTILINE_SINGLE, BASE);
rules << Rule("^.*\"\"\"", quote_format, MULTILINE_DOUBLE, BASE);
// Inside of multiline string
rules << Rule("^.+$", quote_format, MULTILINE_SINGLE, MULTILINE_SINGLE);
rules << Rule("^.+$", quote_format, MULTILINE_DOUBLE, MULTILINE_DOUBLE);
// Regular strings
rules << Rule("\".*?\"", quote_format);
rules << Rule("'.*?'", quote_format);
// String that can be prepended to a regex to make it detect negative
// numbers (but not subtraction). Note that a closing parenthesis is
// needed and the desired number is the last match group.
QString neg = "(^|\\*\\*|[(+\\-=*\\/,\\[])([+\\-\\s]*";
QTextCharFormat float_format;
float_format.setForeground(Colors::yellow);
rules << Rule(neg + "\\b\\d+\\.\\d*)", float_format);
rules << Rule(neg + "\\b\\d+\\.\\d*e\\d+)", float_format);
rules << Rule(neg + "\\b\\d+e\\d+)", float_format);
QTextCharFormat int_format;
int_format.setForeground(Colors::orange);
rules << Rule(neg + "\\b\\d+\\b)", int_format);
QTextCharFormat comment_format;
comment_format.setForeground(Colors::base03);
rules << Rule("#.*", comment_format);
}
////////////////////////////////////////////////////////////////////////////////
void ScriptHighlighter::highlightBlock(const QString& text)
{
int offset = 0;
int state = previousBlockState();
while (offset <= text.length())
{
int match_start = -1;
int match_length;
Rule rule;
for (auto r : rules)
{
if (r.state_in != state)
continue;
auto match = r.regex.match(text, offset);
if (!match.hasMatch())
continue;
auto index = match.lastCapturedIndex();
if (match_start == -1 || match.capturedStart(index) < match_start)
{
match_start = match.capturedStart(index);
match_length = match.capturedLength(index);
rule = r;
}
}
if (match_start == -1)
break;
setFormat(match_start, match_length, rule.format);
offset = match_start + match_length;
state = rule.state_out;
}
setCurrentBlockState(state);
}
|