summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-20 21:11:52 (GMT)
committerIvan Lakhtanov <ivan.lakhtanov@gmail.com>2016-08-24 18:44:30 (GMT)
commit97277343f389fd76ccd5e0cf35e87f2d09957aa8 (patch)
treec4e738ce6050927f46683fe25e51327d29711355
parent890616f6acd65956d5ccfadb140b52de4b9176c8 (diff)
Implementation of syntax highlighting
* Keywords highlight * Simple functions highlight * Correct highlight of oneline/multiline strings and comments - There is no update of variables list Differential Revision: https://phabricator.kde.org/D2015
-rw-r--r--src/backends/julia/CMakeLists.txt3
-rw-r--r--src/backends/julia/juliabackend.cpp2
-rw-r--r--src/backends/julia/juliahighlighter.cpp155
-rw-r--r--src/backends/julia/juliahighlighter.h38
-rw-r--r--src/backends/julia/juliakeywords.cpp86
-rw-r--r--src/backends/julia/juliakeywords.h40
-rw-r--r--src/backends/julia/juliasession.cpp7
-rw-r--r--src/backends/julia/juliasession.h2
-rw-r--r--src/backends/julia/keywords.xml44
9 files changed, 376 insertions, 1 deletions
diff --git a/src/backends/julia/CMakeLists.txt b/src/backends/julia/CMakeLists.txt
index c7d4dc2..760a20a 100644
--- a/src/backends/julia/CMakeLists.txt
+++ b/src/backends/julia/CMakeLists.txt
@@ -5,6 +5,8 @@ set(JuliaBackend_SRCS
juliabackend.cpp
juliasession.cpp
juliaexpression.cpp
+ juliakeywords.cpp
+ juliahighlighter.cpp
)
kconfig_add_kcfg_files(JuliaBackend_SRCS settings.kcfgc)
@@ -15,3 +17,4 @@ add_backend(juliabackend ${JuliaBackend_SRCS})
target_link_libraries(cantor_juliabackend Qt5::DBus)
install(FILES juliabackend.kcfg DESTINATION ${KDE_INSTALL_KCFGDIR})
+install(FILES keywords.xml DESTINATION ${KDE_INSTALL_DATADIR}/cantor/juliabackend)
diff --git a/src/backends/julia/juliabackend.cpp b/src/backends/julia/juliabackend.cpp
index 86679ef..a0eff9d 100644
--- a/src/backends/julia/juliabackend.cpp
+++ b/src/backends/julia/juliabackend.cpp
@@ -43,7 +43,7 @@ Cantor::Session *JuliaBackend::createSession()
Cantor::Backend::Capabilities JuliaBackend::capabilities() const
{
- return Cantor::Backend::Nothing;
+ return Cantor::Backend::SyntaxHighlighting;
}
QString JuliaBackend::description() const
diff --git a/src/backends/julia/juliahighlighter.cpp b/src/backends/julia/juliahighlighter.cpp
new file mode 100644
index 0000000..65b8aaa
--- /dev/null
+++ b/src/backends/julia/juliahighlighter.cpp
@@ -0,0 +1,155 @@
+/*
+ * 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.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+
+#include "juliahighlighter.h"
+#include "juliakeywords.h"
+
+#include <climits>
+#include <QTextEdit>
+
+JuliaHighlighter::JuliaHighlighter(QObject *parent)
+ : Cantor::DefaultHighlighter(parent)
+{
+ addRule(QRegExp(QLatin1String("\\b\\w+(?=\\()")), functionFormat());
+
+ // Code highlighting the different keywords
+ addKeywords(JuliaKeywords::instance()->keywords());
+ addVariables(JuliaKeywords::instance()->variables());
+}
+
+void JuliaHighlighter::highlightBlock(const QString &text)
+{
+ if (skipHighlighting(text)) {
+ return;
+ }
+
+ // Do some backend independent highlighting (brackets etc.)
+ DefaultHighlighter::highlightBlock(text);
+
+ const int IN_MULTILINE_COMMENT = 1;
+ const int IN_CHARACTER = 2;
+ const int IN_SINGLE_QUOTE_STRING = 4;
+ const int IN_TRIPLE_QUOTE_STRING = 8;
+
+ QRegExp multiLineCommentStart(QLatin1String("#="));
+ QRegExp multiLineCommentEnd(QLatin1String("=#"));
+ QRegExp characterStartEnd(QLatin1String("'"));
+ QRegExp singleQuoteStringStartEnd(QLatin1String("\""));
+ QRegExp tripleQuoteStringStartEnd(QLatin1String("\"\"\""));
+ QRegExp singleLineCommentStart(QLatin1String("#(?!=)"));
+
+ int state = previousBlockState();
+ if (state == -1) {
+ state = 0;
+ }
+
+ QList<int> flags = {
+ IN_TRIPLE_QUOTE_STRING,
+ IN_SINGLE_QUOTE_STRING,
+ IN_CHARACTER,
+ IN_MULTILINE_COMMENT
+ };
+ QList<QRegExp> regexps_starts = {
+ tripleQuoteStringStartEnd,
+ singleQuoteStringStartEnd,
+ characterStartEnd,
+ multiLineCommentStart
+ };
+ QList<QRegExp> regexps_ends = {
+ tripleQuoteStringStartEnd,
+ singleQuoteStringStartEnd,
+ characterStartEnd,
+ multiLineCommentEnd
+ };
+ QList<QTextCharFormat> formats = {
+ stringFormat(),
+ stringFormat(),
+ stringFormat(),
+ commentFormat()
+ };
+
+ int pos = 0;
+ while (pos < text.length()) {
+ // Trying to close current environments
+ bool triggered = false;
+ for (int i = 0; i < flags.size() and not triggered; i++) {
+ int flag = flags[i];
+ QRegExp &regexp = regexps_ends[i];
+ QTextCharFormat &format = formats[i];
+ if (state & flag) {
+ int new_pos = regexp.indexIn(text, pos);
+ int length;
+ if (new_pos == -1) {
+ length = text.length() - pos;
+ } else {
+ length = new_pos - pos + regexp.matchedLength();
+ state -= flag;
+ }
+ setFormat(pos, length, format);
+ pos = pos + length;
+ triggered = true;
+ }
+ }
+ if (triggered) {
+ continue;
+ }
+
+ QRegExp *minRegexp = nullptr;
+ int minPos = INT_MAX;
+ int minIdx = -1;
+ for (int i = 0; i < regexps_starts.size(); i++) {
+ QRegExp &regexp = regexps_starts[i];
+ int newPos = regexp.indexIn(text, pos);
+ if (newPos != -1) {
+ minPos = qMin(minPos, newPos);
+ minRegexp = &regexp;
+ minIdx = i;
+ }
+ }
+
+ int singleLineCommentStartPos =
+ singleLineCommentStart.indexIn(text, pos);
+
+ if (singleLineCommentStartPos != -1
+ and singleLineCommentStartPos < minPos) {
+ setFormat(pos, text.length() - pos, commentFormat());
+ break;
+ } else if (minRegexp) {
+ state += flags[minIdx];
+ pos = minPos + minRegexp->matchedLength();
+ setFormat(minPos, minRegexp->matchedLength(), formats[minIdx]);
+ } else {
+ break;
+ }
+ }
+
+ setCurrentBlockState(state);
+}
+
+void JuliaHighlighter::updateHighlight()
+{
+ addVariables(JuliaKeywords::instance()->variables());
+ rehighlight();
+}
+
+QString JuliaHighlighter::nonSeparatingCharacters() const
+{
+ return QLatin1String("[\\w\\u00A1-\\uFFFF!]");
+}
diff --git a/src/backends/julia/juliahighlighter.h b/src/backends/julia/juliahighlighter.h
new file mode 100644
index 0000000..a112732
--- /dev/null
+++ b/src/backends/julia/juliahighlighter.h
@@ -0,0 +1,38 @@
+/*
+ * 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.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include "defaulthighlighter.h"
+
+class JuliaHighlighter: public Cantor::DefaultHighlighter
+{
+ Q_OBJECT
+
+public:
+ JuliaHighlighter(QObject *parent);
+ virtual ~JuliaHighlighter() {}
+
+public Q_SLOTS:
+ void updateHighlight();
+
+protected:
+ virtual void highlightBlock(const QString &text) override;
+ virtual QString nonSeparatingCharacters() const override;
+};
diff --git a/src/backends/julia/juliakeywords.cpp b/src/backends/julia/juliakeywords.cpp
new file mode 100644
index 0000000..e15dbca
--- /dev/null
+++ b/src/backends/julia/juliakeywords.cpp
@@ -0,0 +1,86 @@
+/*
+ * 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.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#include "juliakeywords.h"
+
+#include <QFile>
+#include <QXmlStreamReader>
+#include <QtAlgorithms>
+#include <QStandardPaths>
+#include <QDebug>
+
+JuliaKeywords *JuliaKeywords::instance()
+{
+ static JuliaKeywords *inst = 0;
+ if (inst == 0) {
+ inst = new JuliaKeywords();
+ inst->loadFromFile();
+ qSort(inst->m_keywords);
+ qSort(inst->m_variables);
+ }
+
+ return inst;
+}
+
+void JuliaKeywords::loadFromFile()
+{
+ //load the known keywords from an xml file
+ QFile file(
+ QStandardPaths::locate(
+ QStandardPaths::GenericDataLocation,
+ QLatin1String("cantor/juliabackend/keywords.xml")
+ )
+ );
+
+ if (!file.open(QIODevice::ReadOnly)) {
+ qWarning() << "error opening keywords.xml file. highlighting and"
+ << "completion won't work";
+ return;
+ }
+
+ QXmlStreamReader xml(&file);
+
+ xml.readNextStartElement();
+ while (xml.readNextStartElement()) {
+ const QStringRef name = xml.name();
+
+ if (name == QLatin1String("keywords")
+ or name == QLatin1String("variables")) {
+ while (xml.readNextStartElement()) {
+ Q_ASSERT(
+ xml.isStartElement() and xml.name() == QLatin1String("word")
+ );
+
+ const QString text = xml.readElementText();
+
+ if (name == QLatin1String("keywords")) {
+ m_keywords << text;
+ } else if (name == QLatin1String("variables")) {
+ m_variables << text;
+ }
+ }
+ } else {
+ xml.skipCurrentElement();
+ }
+ }
+
+ if (xml.hasError()) {
+ qWarning() << "Error parsing keywords.xml:" << xml.errorString();
+ }
+}
diff --git a/src/backends/julia/juliakeywords.h b/src/backends/julia/juliakeywords.h
new file mode 100644
index 0000000..74f14b7
--- /dev/null
+++ b/src/backends/julia/juliakeywords.h
@@ -0,0 +1,40 @@
+/*
+ * 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.
+ *
+ * ---
+ * Copyright (C) 2016 Ivan Lakhtanov <ivan.lakhtanov@gmail.com>
+ */
+#pragma once
+
+#include <QStringList>
+
+class JuliaKeywords
+{
+public:
+ static JuliaKeywords *instance();
+
+ const QStringList &keywords() const { return m_keywords; }
+ const QStringList &variables() const { return m_variables; }
+
+private:
+ QStringList m_keywords;
+ QStringList m_variables;
+
+ JuliaKeywords() {}
+ ~JuliaKeywords() {}
+
+ void loadFromFile();
+};
diff --git a/src/backends/julia/juliasession.cpp b/src/backends/julia/juliasession.cpp
index e1f84b9..50dfd42 100644
--- a/src/backends/julia/juliasession.cpp
+++ b/src/backends/julia/juliasession.cpp
@@ -27,6 +27,7 @@
#include "juliaexpression.h"
#include "settings.h"
+#include "juliahighlighter.h"
JuliaSession::JuliaSession(Cantor::Backend *backend)
: Session(backend)
@@ -135,6 +136,12 @@ Cantor::CompletionObject *JuliaSession::completionFor(
return nullptr;
}
+QSyntaxHighlighter *JuliaSession::syntaxHighlighter(QObject *parent)
+{
+ JuliaHighlighter *highlighter = new JuliaHighlighter(parent);
+ return highlighter;
+}
+
void JuliaSession::runJuliaCommand(const QString &command) const
{
m_interface->call(QLatin1String("runJuliaCommand"), command);
diff --git a/src/backends/julia/juliasession.h b/src/backends/julia/juliasession.h
index 9c8a40c..dd2834d 100644
--- a/src/backends/julia/juliasession.h
+++ b/src/backends/julia/juliasession.h
@@ -45,6 +45,8 @@ public:
const QString &cmd,
int index = -1) override;
+ virtual QSyntaxHighlighter *syntaxHighlighter(QObject *parent);
+
private Q_SLOTS:
void onResultReady();
diff --git a/src/backends/julia/keywords.xml b/src/backends/julia/keywords.xml
new file mode 100644
index 0000000..45fe08a
--- /dev/null
+++ b/src/backends/julia/keywords.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<julia>
+ <variables>
+ <word>false</word>
+ <word>Inf</word>
+ <word>NaN</word>
+ <word>nothing</word>
+ <word>true</word>
+ </variables>
+ <keywords>
+ <word>abstract</word>
+ <word>baremodule</word>
+ <word>begin</word>
+ <word>bitstype</word>
+ <word>break</word>
+ <word>catch</word>
+ <word>const</word>
+ <word>continue</word>
+ <word>do</word>
+ <word>elseif</word>
+ <word>else</word>
+ <word>end</word>
+ <word>export</word>
+ <word>finally</word>
+ <word>for</word>
+ <word>function</word>
+ <word>global</word>
+ <word>if</word>
+ <word>immutable</word>
+ <word>importall</word>
+ <word>import</word>
+ <word>let</word>
+ <word>local</word>
+ <word>macro</word>
+ <word>module</word>
+ <word>quote</word>
+ <word>return</word>
+ <word>try</word>
+ <word>typealias</word>
+ <word>type</word>
+ <word>using</word>
+ <word>while</word>
+ </keywords>
+</julia>