diff --git a/src/Charts/RChart.cpp b/src/Charts/RChart.cpp index 153a830d6..d6c025f48 100644 --- a/src/Charts/RChart.cpp +++ b/src/Charts/RChart.cpp @@ -19,6 +19,7 @@ #include "R.h" #include "RTool.h" #include "RChart.h" +#include "RSyntax.h" #include "Colors.h" #include "TabView.h" @@ -285,6 +286,9 @@ RChart::RChart(Context *context) : GcChartWindow(context), context(context) script->setPalette(p); script->setStyleSheet(TabView::ourStyleSheet()); + // syntax highlighter + RSyntax syntax(script->document()); + leftsplitter->addWidget(script); console = new RConsole(context, this); leftsplitter->addWidget(console); @@ -319,7 +323,10 @@ RChart::getScript() const void RChart::setScript(QString string) { - if (rtool && script) script->setText(string); + if (rtool && script) { + script->setText(string); + new RSyntax(script->document()); + } text = string; } diff --git a/src/R/RSyntax.cpp b/src/R/RSyntax.cpp new file mode 100644 index 000000000..285cbe63e --- /dev/null +++ b/src/R/RSyntax.cpp @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include + +#include "RSyntax.h" + + +RSyntax::RSyntax(QTextDocument *parent) : QSyntaxHighlighter(parent) +{ + HighlightingRule rule; + + assignmentFormat.setForeground(QColor(255,204,000)); + rule.pattern = QRegExp("<{1,2}[-]"); + rule.format = assignmentFormat; + highlightingRules.append(rule); + rule.pattern = QRegExp("[-]>{1,2}"); + rule.format = assignmentFormat; + highlightingRules.append(rule); + + + // function: anything followed by ( + functionFormat.setForeground(Qt::cyan); + //functionFormat.setFontItalic(true); + rule.pattern = QRegExp("\\b[A-Za-z0-9_\\.]+(?=[ ]*\\()"); + rule.format = functionFormat; + highlightingRules.append(rule); + + // argument: anything followed by =, but not at the start(???) + // unfortunately look behind assertions are not supported + argumentFormat.setFontItalic(true); + rule.pattern = QRegExp("[A-Za-z0-9_\\.]+(?=[ ]*=[^=])"); + rule.format = argumentFormat; + highlightingRules.append(rule); + + // numbers + numberFormat.setForeground(Qt::red); + QStringList numberPatterns; + numberPatterns << "\\b[0-9]+[\\.]?[0-9]*\\b" << "\\b[\\.][0-9]+\\b"; + foreach (QString pattern, numberPatterns) { + rule.pattern = QRegExp(pattern); + rule.format = numberFormat; + highlightingRules.append(rule); + } + + // constants: TRUE FALSE NA NULL Inf NaN + constantFormat.setForeground(Qt::red); + QStringList constantPatterns; + constantPatterns << "\\bTRUE\\b" << "\\bFALSE\\b" << "\\bNA\\b" << "\\bNULL\\b" << "\\bInf\\b" << "\\bNaN\\b"; + foreach (QString pattern, constantPatterns) { + rule.pattern = QRegExp(pattern); + rule.format = constantFormat; + highlightingRules.append(rule); + } + + // keywords: while for in repeat if else switch break next + // function return message warning stop + keywordFormat.setForeground(QColor(255,204,000)); + //keywordFormat.setFontItalic(true); + QStringList keywordPatterns; + keywordPatterns << "\\bwhile\\b" << "\\bfor\\b" << "\\bin([^%]?)\\b" + << "\\brepeat\\b" << "\\bif\\b" << "\\belse\\b" + << "\\bswitch\\b" << "\\bbreak\\b" << "\\bnext\\b" + << "\\bfunction\\b" << "\\breturn\\b" << "\\bmessage\\b" + << "\\bwarning\\b" << "\\bstop\\b"; + foreach (QString pattern, keywordPatterns) { + rule.pattern = QRegExp(pattern); + rule.format = keywordFormat; + highlightingRules.append(rule); + } + + // common functions (says who? (I hate attach)): library attach + // detach source require + commonFunctionFormat.setForeground(QColor(255,204,000)); + QStringList commonFunctionPatterns; + commonFunctionPatterns << "\\blibrary\\b" << "\\bsource\\b" << "\\brequire\\b"; + foreach (QString pattern, commonFunctionPatterns) { + rule.pattern = QRegExp(pattern); + rule.format = commonFunctionFormat; + highlightingRules.append(rule); + } + + // operators + //operatorFormat.setForeground(Qt::darkCyan); + //operatorFormat.setFontWeight(QFont::Bold); + //QStringList operatorPatterns; + + //operatorPatterns << "[\\&\\$\\@\\|\\:\\~\\{\\}\\(\\)!]" << "==" << "!="; + + //foreach (QString pattern, operatorPatterns) { + //rule.pattern = QRegExp(pattern); + //rule.format = operatorFormat; + //highlightingRules.append(rule); + //} + + // namespace: anything followed by :: + //namespaceFormat.setForeground(Qt::magenta); + //rule.pattern = QRegExp("\\b[A-Za-z0-9_\\.]+(?=::)"); + //rule.format = namespaceFormat; + //highlightingRules.append(rule); + + // quotes: only activated after quotes are closed. Does not + // span lines. + quotationFormat.setForeground(Qt::red); + rule.pattern = QRegExp("\"[^\"]*\""); + rule.format = quotationFormat; + highlightingRules.append(rule); + rule.pattern = QRegExp("'[^']*\'"); + rule.format = quotationFormat; + highlightingRules.append(rule); + + // comments (should override everything else) + commentFormat.setForeground(QColor(100,149,237)); + rule.pattern = QRegExp("#[^\n]*"); + rule.format = commentFormat; + highlightingRules.append(rule); +} + + +void RSyntax::highlightBlock(const QString &text) +{ + foreach (HighlightingRule rule, highlightingRules) { + QRegExp expression(rule.pattern); + int index = text.indexOf(expression); + // NB: this is index in block, not full document + while (index >= 0) { + int length = expression.matchedLength(); + setFormat(index, length, rule.format); + index = text.indexOf(expression, index + length); + } + } +} diff --git a/src/R/RSyntax.h b/src/R/RSyntax.h new file mode 100644 index 000000000..e165c7dc6 --- /dev/null +++ b/src/R/RSyntax.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 Mark Liversedge (liversedge@gmail.com) + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., 51 + * Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef GC_RSyntax +#define GC_RSyntax + +#include + +#include +#include + +class QTextDocument; + +class RSyntax : public QSyntaxHighlighter +{ + Q_OBJECT + + public: + RSyntax(QTextDocument *parent = 0); + + protected: + void highlightBlock(const QString &text); + + private: + struct HighlightingRule { + QRegExp pattern; + QTextCharFormat format; + }; + QVector highlightingRules; + + // initial classifications and values borrowed from ESS + QTextCharFormat assignmentFormat; + QTextCharFormat functionFormat; + QTextCharFormat numberFormat; + QTextCharFormat argumentFormat; + QTextCharFormat constantFormat; + QTextCharFormat keywordFormat; + QTextCharFormat commonFunctionFormat; + QTextCharFormat operatorFormat; + QTextCharFormat namespaceFormat; + QTextCharFormat commentFormat; + QTextCharFormat quotationFormat; +}; + +#endif diff --git a/src/src.pro b/src/src.pro index 072673fea..d8895ec43 100644 --- a/src/src.pro +++ b/src/src.pro @@ -262,8 +262,8 @@ contains(DEFINES, "GC_WANT_R") { DEFINES += STRICT_R_HEADERS ## R integration - HEADERS += R/REmbed.h R/RTool.h R/RGraphicsDevice.h - SOURCES += R/REmbed.cpp R/RTool.cpp R/RGraphicsDevice.cpp + HEADERS += R/REmbed.h R/RTool.h R/RGraphicsDevice.h R/RSyntax.h + SOURCES += R/REmbed.cpp R/RTool.cpp R/RGraphicsDevice.cpp R/RSyntax.cpp ## R based charts HEADERS += Charts/RChart.h Charts/RCanvas.h