diff --git a/autotests/syntaxrepository_test.cpp b/autotests/syntaxrepository_test.cpp index 5013197..8cf9b96 100644 --- a/autotests/syntaxrepository_test.cpp +++ b/autotests/syntaxrepository_test.cpp @@ -1,168 +1,170 @@ /* Copyright (C) 2016 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include namespace SyntaxHighlighting { class NullHighlighter : public AbstractHighlighter { public: using AbstractHighlighter::highlightLine; void setFormat(int offset, int length, const Format &format) Q_DECL_OVERRIDE { Q_UNUSED(offset); Q_UNUSED(length); Q_UNUSED(format); } }; class RepositoryTest : public QObject { Q_OBJECT private: Repository m_repo; private Q_SLOTS: void initTestCase() { QStandardPaths::enableTestMode(true); } void testDefinitionByExtension_data() { QTest::addColumn("fileName"); QTest::addColumn("defName"); QTest::newRow("empty") << QString() << QString(); QTest::newRow("qml") << QStringLiteral("/bla/foo.qml") << QStringLiteral("QML"); QTest::newRow("glsl") << QStringLiteral("flat.frag") << QStringLiteral("GLSL"); // the following ones are defined in multiple syntax definitions QTest::newRow("c") << QStringLiteral("test.c") << QStringLiteral("C"); QTest::newRow("c++") << QStringLiteral("test.cpp") << QStringLiteral("C++"); QTest::newRow("markdown") << QStringLiteral("test.md") << QStringLiteral("Markdown"); QTest::newRow("Makefile 1") << QStringLiteral("Makefile") << QStringLiteral("Makefile"); QTest::newRow("Makefile 2") << QStringLiteral("/some/path/to/Makefile") << QStringLiteral("Makefile"); QTest::newRow("Makefile 3") << QStringLiteral("Makefile.am") << QStringLiteral("Makefile"); } void testDefinitionByExtension() { QFETCH(QString, fileName); QFETCH(QString, defName); auto def = m_repo.definitionForFileName(fileName); if (defName.isEmpty()) { QVERIFY(!def.isValid()); } else { QVERIFY(def.isValid()); QCOMPARE(def.name(), defName); } } void testLoadAll() { foreach (auto def, m_repo.definitions()) { QVERIFY(!def.name().isEmpty()); QVERIFY(!def.translatedName().isEmpty()); QVERIFY(!def.section().isEmpty()); QVERIFY(!def.translatedSection().isEmpty()); // indirectly trigger loading, as we can't reach that from public API // if the loading fails the highlighter will produce empty states NullHighlighter hl; State initialState; hl.setDefinition(def); const auto state = hl.highlightLine(QLatin1String("This should not crash } ] ) !"), initialState); QVERIFY(state != initialState); } } void testMetaData() { auto def = m_repo.definitionForName(QLatin1String("Alerts")); QVERIFY(def.isValid()); QVERIFY(def.extensions().isEmpty()); QVERIFY(def.mimeTypes().isEmpty()); QVERIFY(def.version() >= 1.11f); QVERIFY(def.isHidden()); QCOMPARE(def.section(), QLatin1String("Other")); QCOMPARE(def.license(), QLatin1String("LGPL")); QVERIFY(def.author().contains(QLatin1String("Dominik"))); QFileInfo fi(def.filePath()); QVERIFY(fi.isAbsolute()); QVERIFY(def.filePath().endsWith(QLatin1String("alert.xml"))); def = m_repo.definitionForName(QLatin1String("C++")); QVERIFY(def.isValid()); QCOMPARE(def.section(), QLatin1String("Sources")); QCOMPARE(def.indenter(), QLatin1String("cstyle")); QCOMPARE(def.style(), QLatin1String("C++")); QVERIFY(def.mimeTypes().contains(QLatin1String("text/x-c++hdr"))); QVERIFY(def.extensions().contains(QLatin1String("*.h"))); QCOMPARE(def.priority(), 9); def = m_repo.definitionForName(QLatin1String("Apache Configuration")); QVERIFY(def.isValid()); QVERIFY(def.extensions().contains(QLatin1String("httpd.conf"))); QVERIFY(def.extensions().contains(QLatin1String(".htaccess*"))); } void testReload() { auto def = m_repo.definitionForName(QLatin1String("QML")); QVERIFY(!m_repo.definitions().isEmpty()); QVERIFY(def.isValid()); NullHighlighter hl; hl.setDefinition(def); auto oldState = hl.highlightLine(QLatin1String("/* TODO this should not crash */"), State()); m_repo.reload(); QVERIFY(!m_repo.definitions().isEmpty()); QVERIFY(!def.isValid()); hl.highlightLine(QLatin1String("/* TODO this should not crash */"), State()); hl.highlightLine(QLatin1String("/* FIXME neither should this crash */"), oldState); QVERIFY(hl.definition().isValid()); QCOMPARE(hl.definition().name(), QLatin1String("QML")); } void testThemes() { QVERIFY(!m_repo.themes().isEmpty()); Q_FOREACH (const auto theme, m_repo.themes()) { QVERIFY(theme.isValid()); QVERIFY(!theme.name().isEmpty()); + QVERIFY(!theme.filePath().isEmpty()); + QVERIFY(QFileInfo::exists(theme.filePath())); QVERIFY(m_repo.theme(theme.name()).isValid()); } } }; } QTEST_MAIN(SyntaxHighlighting::RepositoryTest) #include "syntaxrepository_test.moc" diff --git a/src/lib/theme.cpp b/src/lib/theme.cpp index 72ab4b7..40197d7 100644 --- a/src/lib/theme.cpp +++ b/src/lib/theme.cpp @@ -1,108 +1,113 @@ /* Copyright (C) 2016 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "theme.h" #include "themedata_p.h" #include using namespace SyntaxHighlighting; Theme::Theme() { } Theme::Theme(const Theme ©) { m_data = copy.m_data; } Theme::Theme(std::shared_ptr data) : m_data(data) { } Theme::~Theme() { } Theme &Theme::operator=(const Theme &other) { m_data = other.m_data; return *this; } bool Theme::isValid() const { return m_data.get(); } QString Theme::name() const { return m_data ? m_data->name() : QString(); } QString Theme::translatedName() const { return m_data ? QCoreApplication::instance()->translate("Theme", m_data->name().toUtf8().constData()) : QString(); } bool Theme::isReadOnly() const { return m_data ? m_data->isReadOnly() : false; } +QString Theme::filePath() const +{ + return m_data ? m_data->filePath() : QString(); +} + QRgb Theme::textColor(TextStyle style) const { return m_data ? m_data->textColor(style) : 0; } QRgb Theme::selectedTextColor(TextStyle style) const { return m_data ? m_data->selectedTextColor(style) : 0; } QRgb Theme::backgroundColor(TextStyle style) const { return m_data ? m_data->backgroundColor(style) : 0; } QRgb Theme::selectedBackgroundColor(TextStyle style) const { return m_data ? m_data->selectedBackgroundColor(style) : 0; } bool Theme::isBold(TextStyle style) const { return m_data ? m_data->isBold(style) : false; } bool Theme::isItalic(TextStyle style) const { return m_data ? m_data->isItalic(style) : false; } bool Theme::isUnderline(TextStyle style) const { return m_data ? m_data->isUnderline(style) : false; } bool Theme::isStrikeThrough(TextStyle style) const { return m_data ? m_data->isStrikeThrough(style) : false; } diff --git a/src/lib/theme.h b/src/lib/theme.h index 5acf79b..2139f24 100644 --- a/src/lib/theme.h +++ b/src/lib/theme.h @@ -1,208 +1,217 @@ /* Copyright (C) 2016 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SYNTAXHIGHLIGHTING_THEME_H #define SYNTAXHIGHLIGHTING_THEME_H #include "kf5syntaxhighlighting_export.h" #include #include #include namespace SyntaxHighlighting { class ThemeData; /** * Color theme definition used for highlighting. * * @section theme_intro Introduction * * The Theme provides a full color theme for painting the highlighted text. * One Theme is defined either as a *.theme file on disk, or as a file compiled * into the SyntaxHighlighting library by using Qt's resource system. Each * Theme has a unique name(), including a translatedName() if put into the UI. * Themes shipped by default are typically read-only, see isReadOnly(). * * A Theme defines two sets of colors: * - Text colors, including foreground and background colors, colors for * selected text, and properties such as bold and italic. These colors are * used e.g. by the SyntaxHighlighter. * - Editor colors, including a background color for the entire editor widget, * the line number color, code folding colors, etc. * * @section theme_text_colors Text Colors and the Class Format * * The text colors are used for syntax highlighting. * // TODO: elaborate more and explain relation to Format class * * @section theme_editor_colors Editor Colors * * If you want to use the SyntaxHighlighting framework to write your own text * editor, you also need to paint the background of the editing widget. In * addition, the editor may support showing line numbers, a folding bar, a * highlight for the current text line, and similar features. All these colors * are defined in terms of the "editor colors". * * @section theme_access Accessing a Theme * * All available Theme%s are accessed through the Repository. These themes are * typically valid themes. If you create a Theme on your own, isValid() will * return @e false, and all colors provided by this Theme are in fact invalid * and therefore unusable. * * @see Format */ class KF5SYNTAXHIGHLIGHTING_EXPORT Theme { Q_GADGET public: /** Default styles that can be referenced from syntax definition XML files. */ enum TextStyle { Normal = 0, Keyword, Function, Variable, ControlFlow, Operator, BuiltIn, Extension, Preprocessor, Attribute, Char, SpecialChar, String, VerbatimString, SpecialString, Import, DataType, DecVal, BaseN, Float, Constant, Comment, Documentation, Annotation, CommentVar, RegionMarker, Information, Warning, Alert, Error, Others }; Q_ENUM(TextStyle) /** * Default constructor, creating an invalid Theme, see isValid(). */ Theme(); /** * Copy constructor, sharing the Theme data with @p copy. */ Theme(const Theme ©); /** * Destructor. */ ~Theme(); /** * Assignment operator, sharing the Theme data with @p other. */ Theme &operator=(const Theme &other); /** * Returns @c true if this is a valid Theme. * If the theme is invalid, none of the returned colors are well-defined. */ bool isValid() const; /** * Returns the unique name of this Theme. * @see translatedName() */ QString name() const; /** * Returns the translated name of this Theme. The translated name can be * used in the user interface. */ QString translatedName() const; /** * Returns @c true if this Theme is read-only. * Typically, themes that are shipped by default are read-only. */ bool isReadOnly() const; + /** + * Returns the full path and filename to this Theme. + * Themes from the Qt resource return the Qt resource path. + * Themes from disk return the local path. + * + * If the theme is invalid (isValid()), an empty string is returned. + */ + QString filePath() const; + /** * Returns the text color to be used for @p style. * @c 0 is returned for styles that do not specify a text color, * use the default text color in that case. */ QRgb textColor(TextStyle style) const; /** * Returns the selected text color to be used for @p style. * @c 0 is returned for styles that do not specify a selected text color, * use the default textColor() in that case. */ QRgb selectedTextColor(TextStyle style) const; /** * Returns the background color to be used for @p style. * @c 0 is returned for styles that do not specify a background color, * use the default background color in that case. */ QRgb backgroundColor(TextStyle style) const; /** * Returns the background color to be used for selected text for @p style. * @c 0 is returned for styles that do not specify a background color, * use the default backgroundColor() in that case. */ QRgb selectedBackgroundColor(TextStyle style) const; /** Returns whether the given style should be shown in bold. */ bool isBold(TextStyle style) const; /** Returns whether the given style should be shown in italic. */ bool isItalic(TextStyle style) const; /** Returns whether the given style should be shown underlined. */ bool isUnderline(TextStyle style) const; /** Returns whether the given style should be shown striked through. */ bool isStrikeThrough(TextStyle style) const; public: /** * Constructor taking a shared ThemeData instance. */ Theme(std::shared_ptr data); private: /** * Shared data holder. */ std::shared_ptr m_data; }; } #endif // SYNTAXHIGHLIGHTING_THEME_H diff --git a/src/lib/themedata.cpp b/src/lib/themedata.cpp index 75a8a35..77c9217 100644 --- a/src/lib/themedata.cpp +++ b/src/lib/themedata.cpp @@ -1,171 +1,178 @@ /* Copyright (C) 2016 Volker Krause Copyright (C) 2016 Dominik Haumann This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "themedata_p.h" #include #include #include #include #include #include using namespace SyntaxHighlighting; ThemeData::ThemeData() : m_readOnly(true) { } /** * Convert QJsonValue @p val into a color, if possible. Valid colors are only * in hex format: #rrggbb. On error, returns 0x00000000. */ static inline QRgb readColor(const QJsonValue &val) { const QRgb unsetColor = 0; if (!val.isString()) { return unsetColor; } const QString str = val.toString(); if (str.isEmpty() || str[0] != QLatin1Char('#')) { return unsetColor; } const QColor color(str); return color.isValid() ? color.rgb() : unsetColor; } /** * Convert QJsonValue @p val into a bool, if possible. On error, returns false. */ static inline bool readBool(const QJsonValue &val) { return val.isBool() ? val.toBool() : false; } static inline TextStyleData readThemeData(const QJsonObject &obj) { TextStyleData td; td.textColor = readColor(obj.value(QLatin1String("text-color"))); td.backgroundColor = readColor(obj.value(QLatin1String("background-color"))); td.selectedTextColor = readColor(obj.value(QLatin1String("selected-text-color"))); td.selectedBackgroundColor = readColor(obj.value(QLatin1String("selected-background-color"))); td.bold = readBool(obj.value(QLatin1String("bold"))); td.italic = readBool(obj.value(QLatin1String("italic"))); td.underline = readBool(obj.value(QLatin1String("underline"))); td.strikeThrough = readBool(obj.value(QLatin1String("strike-through"))); return td; } bool ThemeData::load(const QString &filePath) { QFile loadFile(filePath); if (!loadFile.open(QIODevice::ReadOnly)) { return false; } const QByteArray jsonData = loadFile.readAll(); QJsonParseError parseError; QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); if (parseError.error != QJsonParseError::NoError) { return false; } + m_filePath = filePath; + QJsonObject obj = jsonDoc.object(); // read metadata const QJsonObject metadata = obj.value(QLatin1String("metadata")).toObject(); m_name = metadata.value(QLatin1String("name")).toString(); m_revision = metadata.value(QLatin1String("revision")).toInt(); m_author = metadata.value(QLatin1String("author")).toString(); m_license = metadata.value(QLatin1String("license")).toString(); m_readOnly = metadata.value(QLatin1String("read-only")).toBool(); // read text styles static const auto idx = Theme::staticMetaObject.indexOfEnumerator("TextStyle"); Q_ASSERT(idx >= 0); const auto metaEnum = Theme::staticMetaObject.enumerator(idx); const QJsonObject textStyles = obj.value(QLatin1String("text-styles")).toObject(); for (int i = 0; i < metaEnum.keyCount(); ++i) { Q_ASSERT(i == metaEnum.value(i)); m_textStyles[i] = readThemeData(textStyles.value(QLatin1String(metaEnum.key(i))).toObject()); } return true; } QString ThemeData::name() const { return m_name; } bool ThemeData::isReadOnly() const { return m_readOnly; } +QString ThemeData::filePath() const +{ + return m_filePath; +} + QRgb ThemeData::textColor(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].textColor; } QRgb ThemeData::selectedTextColor(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].selectedTextColor; } QRgb ThemeData::backgroundColor(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].backgroundColor; } QRgb ThemeData::selectedBackgroundColor(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].selectedBackgroundColor; } bool ThemeData::isBold(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].bold; } bool ThemeData::isItalic(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].italic; } bool ThemeData::isUnderline(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].underline; } bool ThemeData::isStrikeThrough(Theme::TextStyle style) const { Q_ASSERT(static_cast(style) >= 0 && static_cast(style) <= static_cast(Theme::Others)); return m_textStyles[style].strikeThrough; } diff --git a/src/lib/themedata_p.h b/src/lib/themedata_p.h index 7f75b14..c131fe0 100644 --- a/src/lib/themedata_p.h +++ b/src/lib/themedata_p.h @@ -1,129 +1,139 @@ /* Copyright (C) 2016 Volker Krause Copyright (C) 2016 Dominik Haumann This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library 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 Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef SYNTAXHIGHLIGHTING_THEMEDATA_H #define SYNTAXHIGHLIGHTING_THEMEDATA_H #include "theme.h" namespace SyntaxHighlighting { class TextStyleData { public: // Constructor initializing all data. TextStyleData() : textColor(0x0) , backgroundColor(0x0) , selectedTextColor(0x0) , selectedBackgroundColor(0x0) , bold(false) , italic(false) , underline(false) , strikeThrough(false) {} QRgb textColor; QRgb backgroundColor; QRgb selectedTextColor; QRgb selectedBackgroundColor; bool bold; bool italic; bool underline; bool strikeThrough; }; /** * Data container for a Theme. */ class ThemeData { public: /** * Default constructor, creating an uninitialized ThemeData instance. */ ThemeData(); /** * Load the Theme data from the file @p filePath. * Note, that @p filePath either is a local file, or a qt resource location. */ bool load(const QString &filePath); /** * Returns the unique name of this Theme. */ QString name() const; /** * Returns @c true if this Theme is read-only. * Typically, themes that are shipped by default are read-only. */ bool isReadOnly() const; + /** + * Returns the full path and filename to this Theme. + * Themes from the Qt resource return the Qt resource path. + * Themes from disk return the local path. + * + * If the theme is invalid (isValid()), an empty string is returned. + */ + QString filePath() const; + /** * Returns the text color to be used for @p style. * @c 0 is returned for styles that do not specify a text color, * use the default text color in that case. */ QRgb textColor(Theme::TextStyle style) const; /** * Returns the text color for selected to be used for @p style. * @c 0 is returned for styles that do not specify a selected text color, * use the textColor() in that case. */ QRgb selectedTextColor(Theme::TextStyle style) const; /** * Returns the background color to be used for @p style. * @c 0 is returned for styles that do not specify a background color, * use the default background color in that case. */ QRgb backgroundColor(Theme::TextStyle style) const; /** * Returns the background color for selected text to be used for @p style. * @c 0 is returned for styles that do not specify a selected background * color, use the default backgroundColor() in that case. */ QRgb selectedBackgroundColor(Theme::TextStyle style) const; /** Returns whether the given style should be shown in bold. */ bool isBold(Theme::TextStyle style) const; /** Returns whether the given style should be shown in italic. */ bool isItalic(Theme::TextStyle style) const; /** Returns whether the given style should be shown underlined. */ bool isUnderline(Theme::TextStyle style) const; /** Returns whether the given style should be shown striked through. */ bool isStrikeThrough(Theme::TextStyle style) const; private: int m_revision; QString m_name; QString m_author; QString m_license; + QString m_filePath; bool m_readOnly; TextStyleData m_textStyles[Theme::Others + 1]; }; } Q_DECLARE_TYPEINFO(SyntaxHighlighting::TextStyleData, Q_MOVABLE_TYPE); #endif // SYNTAXHIGHLIGHTING_THEMEDATA_H