diff options
author | David Schulz <david.schulz@qt.io> | 2018-11-08 11:39:48 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2019-01-28 11:37:08 +0000 |
commit | 14834e6b0a4e7c1272ec3a1838b0634dd66e72ea (patch) | |
tree | 23caed8a748c0d38c59e1f92b448551122d11df2 /src/libs/3rdparty/syntax-highlighting/src/lib | |
parent | 1c77d7e1a79f59485eb90c066d17ec7436f46a93 (diff) |
TextEditor: replace generic highlighter with ksyntaxhighlighting
Fixes: QTCREATORBUG-21029
Change-Id: I9894c4384e0e47da6bf030b7b8e07c3ad4737ff3
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
Diffstat (limited to 'src/libs/3rdparty/syntax-highlighting/src/lib')
43 files changed, 7406 insertions, 0 deletions
diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt new file mode 100644 index 0000000000..bf729fca71 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt @@ -0,0 +1,86 @@ +ecm_create_qm_loader(syntax_highlighting_QM_LOADER syntaxhighlighting5_qt) + +set(syntax_highlighting_srcs + abstracthighlighter.cpp + context.cpp + contextswitch.cpp + definitiondownloader.cpp + foldingregion.cpp + format.cpp + htmlhighlighter.cpp + keywordlist.cpp + rule.cpp + definition.cpp + repository.cpp + state.cpp + syntaxhighlighter.cpp + theme.cpp + wildcardmatcher.cpp + themedata.cpp + ${syntax_highlighting_QM_LOADER} +) +ecm_qt_declare_logging_category(syntax_highlighting_srcs + HEADER ksyntaxhighlighting_logging.h + IDENTIFIER KSyntaxHighlighting::Log + CATEGORY_NAME org.kde.ksyntaxhighlighting +) + +add_library(KF5SyntaxHighlighting ${syntax_highlighting_srcs} $<TARGET_OBJECTS:SyntaxHighlightingData>) +generate_export_header(KF5SyntaxHighlighting BASE_NAME KSyntaxHighlighting) +set_target_properties(KF5SyntaxHighlighting PROPERTIES + VERSION ${SyntaxHighlighting_VERSION_STRING} + SOVERSION ${SyntaxHighlighting_SOVERSION} + EXPORT_NAME SyntaxHighlighting +) +target_include_directories(KF5SyntaxHighlighting INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting;${KDE_INSTALL_INCLUDEDIR_KF5}>") +target_include_directories(KF5SyntaxHighlighting PUBLIC "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR};${CMAKE_CURRENT_BINARY_DIR};>") +target_link_libraries(KF5SyntaxHighlighting LINK_PUBLIC Qt5::Gui LINK_PRIVATE Qt5::Network) + +ecm_generate_headers(SyntaxHighlighting_HEADERS + HEADER_NAMES + AbstractHighlighter + Definition + FoldingRegion + Format + Repository + State + SyntaxHighlighter + Theme + REQUIRED_HEADERS SyntaxHighlighting_HEADERS +) + +install(TARGETS KF5SyntaxHighlighting EXPORT KF5SyntaxHighlightingTargets ${INSTALL_TARGETS_DEFAULT_ARGS}) +install(FILES + ${SyntaxHighlighting_HEADERS} + ${CMAKE_CURRENT_BINARY_DIR}/ksyntaxhighlighting_export.h + DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting) + +if(BUILD_QCH) + ecm_add_qch( + KF5SyntaxHighlighting_QCH + NAME KSyntaxHighlighting + BASE_NAME KF5SyntaxHighlighting + VERSION ${KF5_VERSION} + ORG_DOMAIN org.kde + SOURCES # using only public headers, to cover only public API + ${SyntaxHighlighting_HEADERS} + MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md" + LINK_QCHS + Qt5Core_QCH + Qt5Gui_QCH + BLANK_MACROS + KSYNTAXHIGHLIGHTING_EXPORT + KSYNTAXHIGHLIGHTING_DEPRECATED + TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR} + COMPONENT Devel + ) +endif() +ecm_generate_pri_file( + BASE_NAME KSyntaxHighlighting LIB_NAME + KF5SyntaxHighlighting + DEPS "gui" + FILENAME_VAR PRI_FILENAME + INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/KSyntaxHighlighting +) +install(FILES ${PRI_FILENAME} DESTINATION ${ECM_MKSPECS_INSTALL_DIR}) diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp new file mode 100644 index 0000000000..f69944debd --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp @@ -0,0 +1,328 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "abstracthighlighter.h" +#include "abstracthighlighter_p.h" +#include "context_p.h" +#include "definition_p.h" +#include "foldingregion.h" +#include "format.h" +#include "repository.h" +#include "rule_p.h" +#include "state.h" +#include "state_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "theme.h" + +using namespace KSyntaxHighlighting; + +AbstractHighlighterPrivate::AbstractHighlighterPrivate() +{ +} + +AbstractHighlighterPrivate::~AbstractHighlighterPrivate() +{ +} + +void AbstractHighlighterPrivate::ensureDefinitionLoaded() +{ + auto defData = DefinitionData::get(m_definition); + if (Q_UNLIKELY(!m_definition.isValid() && defData->repo && !m_definition.name().isEmpty())) { + qCDebug(Log) << "Definition became invalid, trying re-lookup."; + m_definition = defData->repo->definitionForName(m_definition.name()); + defData = DefinitionData::get(m_definition); + } + + if (Q_UNLIKELY(!defData->repo && !defData->name.isEmpty())) + qCCritical(Log) << "Repository got deleted while a highlighter is still active!"; + + if (m_definition.isValid()) + defData->load(); +} + +AbstractHighlighter::AbstractHighlighter() : + d_ptr(new AbstractHighlighterPrivate) +{ +} + +AbstractHighlighter::AbstractHighlighter(AbstractHighlighterPrivate *dd) : + d_ptr(dd) +{ +} + +AbstractHighlighter::~AbstractHighlighter() +{ + delete d_ptr; +} + +Definition AbstractHighlighter::definition() const +{ + return d_ptr->m_definition; +} + +void AbstractHighlighter::setDefinition(const Definition &def) +{ + Q_D(AbstractHighlighter); + d->m_definition = def; +} + +Theme AbstractHighlighter::theme() const +{ + Q_D(const AbstractHighlighter); + return d->m_theme; +} + +void AbstractHighlighter::setTheme(const Theme &theme) +{ + Q_D(AbstractHighlighter); + d->m_theme = theme; +} + +/** + * Returns the index of the first non-space character. If the line is empty, + * or only contains white spaces, text.size() is returned. + */ +static inline int firstNonSpaceChar(const QString & text) +{ + for (int i = 0; i < text.length(); ++i) { + if (!text[i].isSpace()) { + return i; + } + } + return text.size(); +} + +State AbstractHighlighter::highlightLine(const QString& text, const State &state) +{ + Q_D(AbstractHighlighter); + + // verify definition, deal with no highlighting being enabled + d->ensureDefinitionLoaded(); + if (!d->m_definition.isValid()) { + applyFormat(0, text.size(), Format()); + return State(); + } + + // verify/initialize state + auto defData = DefinitionData::get(d->m_definition); + auto newState = state; + auto stateData = StateData::get(newState); + const DefinitionRef currentDefRef(d->m_definition); + if (!stateData->isEmpty() && (stateData->m_defRef != currentDefRef)) { + qCDebug(Log) << "Got invalid state, resetting."; + stateData->clear(); + } + if (stateData->isEmpty()) { + stateData->push(defData->initialContext(), QStringList()); + stateData->m_defRef = currentDefRef; + } + + // process empty lines + if (text.isEmpty()) { + while (!stateData->topContext()->lineEmptyContext().isStay()) { + if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList())) + break; + } + auto context = stateData->topContext(); + applyFormat(0, 0, context->attributeFormat()); + return newState; + } + + int offset = 0, beginOffset = 0; + bool lineContinuation = false; + QHash<Rule*, int> skipOffsets; + + /** + * current active format + * stored as pointer to avoid deconstruction/constructions inside the internal loop + * the pointers are stable, the formats are either in the contexts or rules + */ + auto currentFormat = &stateData->topContext()->attributeFormat(); + + /** + * cached first non-space character, needs to be computed if < 0 + */ + int firstNonSpace = -1; + int lastOffset = offset; + int endlessLoopingCounter = 0; + do { + /** + * avoid that we loop endless for some broken hl definitions + */ + if (lastOffset == offset) { + ++endlessLoopingCounter; + if (endlessLoopingCounter > 1024) { + qCDebug(Log) << "Endless state transitions, aborting highlighting of line."; + break; + } + } else { + // ensure we made progress, clear the endlessLoopingCounter + Q_ASSERT(offset > lastOffset); + lastOffset = offset; + endlessLoopingCounter = 0; + } + + /** + * try to match all rules in the context in order of declaration in XML + */ + bool isLookAhead = false; + int newOffset = 0; + const Format *newFormat = nullptr; + for (const auto &rule : stateData->topContext()->rules()) { + /** + * filter out rules that require a specific column + */ + if ((rule->requiredColumn() >= 0) && (rule->requiredColumn() != offset)) { + continue; + } + + /** + * filter out rules that only match for leading whitespace + */ + if (rule->firstNonSpace()) { + /** + * compute the first non-space lazy + * avoids computing it for contexts without any such rules + */ + if (firstNonSpace < 0) { + firstNonSpace = firstNonSpaceChar(text); + } + + /** + * can we skip? + */ + if (offset > firstNonSpace) { + continue; + } + } + + /** + * shall we skip application of this rule? two cases: + * - rule can't match at all => currentSkipOffset < 0 + * - rule will only match for some higher offset => currentSkipOffset > offset + */ + const auto currentSkipOffset = skipOffsets.value(rule.get()); + if (currentSkipOffset < 0 || currentSkipOffset > offset) + continue; + + + const auto newResult = rule->doMatch(text, offset, stateData->topCaptures()); + newOffset = newResult.offset(); + + /** + * update skip offset if new one rules out any later match or is larger than current one + */ + if (newResult.skipOffset() < 0 || newResult.skipOffset() > currentSkipOffset) + skipOffsets.insert(rule.get(), newResult.skipOffset()); + + if (newOffset <= offset) + continue; + + // apply folding + if (rule->endRegion().isValid()) + applyFolding(offset, newOffset - offset, rule->endRegion()); + if (rule->beginRegion().isValid()) + applyFolding(offset, newOffset - offset, rule->beginRegion()); + + if (rule->isLookAhead()) { + Q_ASSERT(!rule->context().isStay()); + d->switchContext(stateData, rule->context(), newResult.captures()); + isLookAhead = true; + break; + } + + d->switchContext(stateData, rule->context(), newResult.captures()); + newFormat = rule->attributeFormat().isValid() ? &rule->attributeFormat() : &stateData->topContext()->attributeFormat(); + if (newOffset == text.size() && std::dynamic_pointer_cast<LineContinue>(rule)) + lineContinuation = true; + break; + } + if (isLookAhead) + continue; + + if (newOffset <= offset) { // no matching rule + if (stateData->topContext()->fallthrough()) { + d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList()); + continue; + } + + newOffset = offset + 1; + newFormat = &stateData->topContext()->attributeFormat(); + } + + /** + * if we arrive here, some new format has to be set! + */ + Q_ASSERT(newFormat); + + /** + * on format change, apply the last one and switch to new one + */ + if (newFormat != currentFormat && newFormat->id() != currentFormat->id()) { + if (offset > 0) + applyFormat(beginOffset, offset - beginOffset, *currentFormat); + beginOffset = offset; + currentFormat = newFormat; + } + + /** + * we must have made progress if we arrive here! + */ + Q_ASSERT(newOffset > offset); + offset = newOffset; + + } while (offset < text.size()); + + if (beginOffset < offset) + applyFormat(beginOffset, text.size() - beginOffset, *currentFormat); + + while (!stateData->topContext()->lineEndContext().isStay() && !lineContinuation) { + if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList())) + break; + } + + return newState; +} + +bool AbstractHighlighterPrivate::switchContext(StateData *data, const ContextSwitch &contextSwitch, const QStringList &captures) +{ + // kill as many items as requested from the stack, will always keep the initial context alive! + const bool initialContextSurvived = data->pop(contextSwitch.popCount()); + + // if we have a new context to add, push it + // then we always "succeed" + if (contextSwitch.context()) { + data->push(contextSwitch.context(), captures); + return true; + } + + // else we abort, if we did try to pop the initial context + return initialContextSurvived; +} + +void AbstractHighlighter::applyFolding(int offset, int length, FoldingRegion region) +{ + Q_UNUSED(offset); + Q_UNUSED(length); + Q_UNUSED(region); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h new file mode 100644 index 0000000000..2b88729697 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h @@ -0,0 +1,195 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H +#define KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H + +#include "ksyntaxhighlighting_export.h" + +#include <QObject> + +#include <memory> + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class AbstractHighlighterPrivate; +class Definition; +class FoldingRegion; +class Format; +class State; +class Theme; + +/** + * Abstract base class for highlighters. + * + * @section abshl_intro Introduction + * + * The AbstractHighlighter provides an interface to highlight text. + * + * The SyntaxHighlighting framework already ships with one implementation, + * namely the SyntaxHighlighter, which also derives from QSyntaxHighlighter, + * meaning that it can be used to highlight a QTextDocument or a QML TextEdit. + * In order to use the SyntaxHighlighter, just call setDefinition() and + * setTheme(), and the associated documents will automatically be highlighted. + * + * However, if you want to use the SyntaxHighlighting framework to implement + * your own syntax highlighter, you need to sublcass from AbstractHighlighter. + * + * @section abshl_impl Implementing your own Syntax Highlighter + * + * In order to implement your own syntax highlighter, you need to inherit from + * AbstractHighlighter. Then, pass each text line that needs to be highlighted + * in order to highlightLine(). Internally, highlightLine() uses the Definition + * initially set through setDefinition() and the State of the previous text line + * to parse and highlight the given text line. For each visual highlighting + * change, highlightLine() will call applyFormat(). Therefore, reimplement + * applyFormat() to get notified of the Format that is valid in the range + * starting at the given offset with the specified length. Similarly, for each + * text part that starts or ends a code folding region, highlightLine() will + * call applyFolding(). Therefore, if you are interested in code folding, + * reimplement applyFolding() to get notified of the starting and ending code + * folding regions, again specified in the range starting at the given offset + * with the given length. + * + * The Format class itself depends on the current Theme. A theme must be + * initially set once such that the Format%s instances can be queried for + * concrete colors. + * + * Optionally, you can also reimplement setTheme() and setDefinition() to get + * notified whenever the Definition or the Theme changes. + * + * @see SyntaxHighlighter + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT AbstractHighlighter +{ +public: + virtual ~AbstractHighlighter(); + + /** + * Returns the syntax definition used for highlighting. + * + * @see setDefinition() + */ + Definition definition() const; + + /** + * Sets the syntax definition used for highlighting. + * + * Subclasses can re-implement this method to e.g. trigger + * re-highlighting or clear internal data structures if needed. + */ + virtual void setDefinition(const Definition &def); + + /** + * Returns the currently selected theme for highlighting. + * + * @note If no Theme was set through setTheme(), the returned Theme will be + * invalid, see Theme::isValid(). + */ + Theme theme() const; + + /** + * Sets the theme used for highlighting. + * + * Subclasses can re-implement this method to e.g. trigger + * re-highlighing or to do general palette color setup. + */ + virtual void setTheme(const Theme &theme); + +protected: + AbstractHighlighter(); + AbstractHighlighter(AbstractHighlighterPrivate *dd); + + // TODO KF6: add an optional void* context argument that is passed through + // to the applyX() calls, so highlighters dealing with some form of line object + // (such as QSyntaxHighlighter or KTextEditor) can avoid some ugly hacks to have + // this context available in their applyX methods + /** + * Highlight the given line. Call this from your derived class + * where appropriate. This will result in any number of applyFormat() + * and applyFolding() calls as a result. + * @param text A string containing the text of the line to highlight. + * @param state The highlighting state handle returned by the call + * to highlightLine() for the previous line. For the very first line, + * just pass a default constructed State(). + * @returns The state of the highlighing engine after processing the + * given line. This needs to passed into highlightLine() for the + * next line. You can store the state for efficient partial + * re-highlighting for example during editing. + * + * @see applyFormat(), applyFolding() + */ + State highlightLine(const QString &text, const State &state); + + /** + * Reimplement this to apply formats to your output. The provided @p format + * is valid for the interval [@p offset, @p offset + @p length). + * + * @param offset The start column of the interval for which @p format matches + * @param length The length of the matching text + * @param format The Format that applies to the range [offset, offset + length) + * + * @note Make sure to set a valid Definition, otherwise the parameter + * @p format is invalid for the entire line passed to highlightLine() + * (cf. Format::isValid()). + * + * @see applyFolding(), highlightLine() + */ + virtual void applyFormat(int offset, int length, const Format &format) = 0; + + /** + * Reimplement this to apply folding to your output. The provided + * FoldingRegion @p region either stars or ends a code folding region in the + * interval [@p offset, @p offset + @p length). + * + * @param offset The start column of the FoldingRegion + * @param length The length of the matching text that starts / ends a + * folding region + * @param region The FoldingRegion that applies to the range [offset, offset + length) + * + * @note The FoldingRegion @p region is @e always either of type + * FoldingRegion::Type::Begin or FoldingRegion::Type::End. + * + * @see applyFormat(), highlightLine(), FoldingRegion + */ + virtual void applyFolding(int offset, int length, FoldingRegion region); + +protected: + AbstractHighlighterPrivate *d_ptr; + +private: + Q_DECLARE_PRIVATE(AbstractHighlighter) + Q_DISABLE_COPY(AbstractHighlighter) +}; +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(KSyntaxHighlighting::AbstractHighlighter, "org.kde.SyntaxHighlighting.AbstractHighlighter") +QT_END_NAMESPACE + +#endif // KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h new file mode 100644 index 0000000000..bdfdf23bc1 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h @@ -0,0 +1,54 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTER_P_H +#define KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTER_P_H + +#include "definition.h" +#include "theme.h" + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class ContextSwitch; +class StateData; + +class AbstractHighlighterPrivate +{ +public: + AbstractHighlighterPrivate(); + virtual ~AbstractHighlighterPrivate(); + + void ensureDefinitionLoaded(); + bool switchContext(StateData* data, const ContextSwitch &contextSwitch, const QStringList &captures); + + Definition m_definition; + Theme m_theme; +}; + +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp new file mode 100644 index 0000000000..9887b959d0 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp @@ -0,0 +1,206 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "context_p.h" +#include "definition_p.h" +#include "format.h" +#include "repository.h" +#include "rule_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "xml_p.h" + +#include <QString> +#include <QXmlStreamReader> + +using namespace KSyntaxHighlighting; + +Definition Context::definition() const +{ + return m_def.definition(); +} + +void Context::setDefinition(const DefinitionRef &def) +{ + m_def = def; +} + +bool Context::indentationBasedFoldingEnabled() const +{ + if (m_noIndentationBasedFolding) + return false; + + return m_def.definition().indentationBasedFoldingEnabled(); +} + +void Context::load(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("context")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + m_name = reader.attributes().value(QStringLiteral("name")).toString(); + m_attribute = reader.attributes().value(QStringLiteral("attribute")).toString(); + m_lineEndContext.parse(reader.attributes().value(QStringLiteral("lineEndContext"))); + m_lineEmptyContext.parse(reader.attributes().value(QStringLiteral("lineEmptyContext"))); + m_fallthrough = Xml::attrToBool(reader.attributes().value(QStringLiteral("fallthrough"))); + m_fallthroughContext.parse(reader.attributes().value(QStringLiteral("fallthroughContext"))); + if (m_fallthroughContext.isStay()) + m_fallthrough = false; + m_noIndentationBasedFolding = Xml::attrToBool(reader.attributes().value(QStringLiteral("noIndentationBasedFolding"))); + + reader.readNext(); + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + { + auto rule = Rule::create(reader.name()); + if (rule) { + rule->setDefinition(m_def.definition()); + if (rule->load(reader)) + m_rules.push_back(rule); + } else { + reader.skipCurrentElement(); + } + reader.readNext(); + break; + } + case QXmlStreamReader::EndElement: + return; + default: + reader.readNext(); + break; + } + } +} + +void Context::resolveContexts() +{ + const auto def = m_def.definition(); + m_lineEndContext.resolve(def); + m_lineEmptyContext.resolve(def); + m_fallthroughContext.resolve(def); + for (const auto &rule : m_rules) + rule->resolveContext(); +} + +Context::ResolveState Context::resolveState() +{ + if (m_resolveState == Unknown) { + for (const auto &rule : m_rules) { + auto inc = std::dynamic_pointer_cast<IncludeRules>(rule); + if (inc) { + m_resolveState = Unresolved; + return m_resolveState; + } + } + m_resolveState = Resolved; + } + return m_resolveState; +} + +void Context::resolveIncludes() +{ + if (resolveState() == Resolved) + return; + if (resolveState() == Resolving) { + qCWarning(Log) << "Cyclic dependency!"; + return; + } + + Q_ASSERT(resolveState() == Unresolved); + m_resolveState = Resolving; // cycle guard + + for (auto it = m_rules.begin(); it != m_rules.end();) { + auto inc = std::dynamic_pointer_cast<IncludeRules>(*it); + if (!inc) { + ++it; + continue; + } + Context* context = nullptr; + auto myDefData = DefinitionData::get(m_def.definition()); + if (inc->definitionName().isEmpty()) { // local include + context = myDefData->contextByName(inc->contextName()); + } else { + auto def = myDefData->repo->definitionForName(inc->definitionName()); + if (!def.isValid()) { + qCWarning(Log) << "Unable to resolve external include rule for definition" << inc->definitionName() << "in" << m_def.definition().name(); + ++it; + continue; + } + auto defData = DefinitionData::get(def); + defData->load(); + if (inc->contextName().isEmpty()) + context = defData->initialContext(); + else + context = defData->contextByName(inc->contextName()); + } + if (!context) { + qCWarning(Log) << "Unable to resolve include rule for definition" << inc->contextName() << "##" << inc->definitionName() << "in" << m_def.definition().name(); + ++it; + continue; + } + context->resolveIncludes(); + + /** + * handle included attribute + * transitive closure: we might include attributes included from somewhere else + */ + if (inc->includeAttribute()) { + m_attribute = context->m_attribute; + m_attributeContext = context->m_attributeContext ? context->m_attributeContext : context; + } + + it = m_rules.erase(it); + for (const auto &rule : context->rules()) { + it = m_rules.insert(it, rule); + ++it; + } + } + + m_resolveState = Resolved; +} + +void Context::resolveAttributeFormat() +{ + /** + * try to get our format from the definition we stem from + * we need to handle included attributes via m_attributeContext + */ + if (!m_attribute.isEmpty()) { + const auto def = m_attributeContext ? m_attributeContext->m_def.definition() : m_def.definition(); + m_attributeFormat = DefinitionData::get(def)->formatByName(m_attribute); + if (!m_attributeFormat.isValid()) { + if (m_attributeContext) { + qCWarning(Log) << "Context: Unknown format" << m_attribute << "in context" << m_name << "of definition" << m_def.definition().name() << "from included context" << m_attributeContext->m_name << "of definition" << def.name(); + } else { + qCWarning(Log) << "Context: Unknown format" << m_attribute << "in context" << m_name << "of definition" << m_def.definition().name(); + } + } + } + + /** + * lookup formats for our rules + */ + for (const auto &rule : m_rules) { + rule->resolveAttributeFormat(this); + } +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h new file mode 100644 index 0000000000..a034d0e27d --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h @@ -0,0 +1,139 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_CONTEXT_P_H +#define KSYNTAXHIGHLIGHTING_CONTEXT_P_H + +#include "rule_p.h" +#include "contextswitch_p.h" +#include "definition.h" +#include "definitionref_p.h" +#include "format.h" + +#include <QString> + +#include <vector> + +QT_BEGIN_NAMESPACE +class QXmlStreamReader; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class Context +{ +public: + Context() = default; + ~Context() = default; + + Definition definition() const; + void setDefinition(const DefinitionRef &def); + + const QString &name() const + { + return m_name; + } + + const ContextSwitch &lineEndContext() const + { + return m_lineEndContext; + } + + const ContextSwitch &lineEmptyContext() const + { + return m_lineEmptyContext; + } + + bool fallthrough() const + { + return m_fallthrough; + } + + const ContextSwitch &fallthroughContext() const + { + return m_fallthroughContext; + } + + const Format &attributeFormat() const + { + return m_attributeFormat; + } + + const std::vector<Rule::Ptr> &rules() const + { + return m_rules; + } + + /** + * Returns @c true, when indentationBasedFolding is enabled for the + * associated Definition and when "noIndentationBasedFolding" is NOT set. + */ + bool indentationBasedFoldingEnabled() const; + + void load(QXmlStreamReader &reader); + void resolveContexts(); + void resolveIncludes(); + void resolveAttributeFormat(); + +private: + Q_DISABLE_COPY(Context) + + enum ResolveState { + Unknown, + Unresolved, + Resolving, + Resolved + }; + ResolveState resolveState(); + + DefinitionRef m_def; + QString m_name; + + /** + * attribute name, to lookup our format + */ + QString m_attribute; + + /** + * context to use for lookup, if != this context + */ + const Context *m_attributeContext = nullptr; + + /** + * resolved format for our attribute, done in resolveAttributeFormat + */ + Format m_attributeFormat; + + ContextSwitch m_lineEndContext; + ContextSwitch m_lineEmptyContext; + ContextSwitch m_fallthroughContext; + + std::vector<Rule::Ptr> m_rules; + + ResolveState m_resolveState = Unknown; + bool m_fallthrough = false; + bool m_noIndentationBasedFolding = false; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_CONTEXT_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp new file mode 100644 index 0000000000..a4d1dbbd85 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch.cpp @@ -0,0 +1,90 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "contextswitch_p.h" +#include "definition.h" +#include "definition_p.h" +#include "repository.h" +#include "ksyntaxhighlighting_logging.h" + + +using namespace KSyntaxHighlighting; + +bool ContextSwitch::isStay() const +{ + return m_popCount == 0 && !m_context && m_contextName.isEmpty() && m_defName.isEmpty(); +} + +int ContextSwitch::popCount() const +{ + return m_popCount; +} + +Context* ContextSwitch::context() const +{ + return m_context; +} + +void ContextSwitch::parse(const QStringRef& contextInstr) +{ + if (contextInstr.isEmpty() || contextInstr == QLatin1String("#stay")) + return; + + if (contextInstr.startsWith(QLatin1String("#pop!"))) { + ++m_popCount; + m_contextName = contextInstr.mid(5).toString(); + return; + } + + if (contextInstr.startsWith(QLatin1String("#pop"))) { + ++m_popCount; + parse(contextInstr.mid(4)); + return; + } + + const auto idx = contextInstr.indexOf(QLatin1String("##")); + if (idx >= 0) { + m_contextName = contextInstr.left(idx).toString(); + m_defName = contextInstr.mid(idx + 2).toString(); + } else { + m_contextName = contextInstr.toString(); + } +} + +void ContextSwitch::resolve(const Definition &def) +{ + auto d = def; + if (!m_defName.isEmpty()) { + d = DefinitionData::get(def)->repo->definitionForName(m_defName); + auto data = DefinitionData::get(d); + data->load(); + if (m_contextName.isEmpty()) + m_context = data->initialContext(); + } + + if (!m_contextName.isEmpty()) { + m_context = DefinitionData::get(d)->contextByName(m_contextName); + if (!m_context) + qCWarning(Log) << "cannot find context" << m_contextName << "in" << def.name(); + } +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h new file mode 100644 index 0000000000..669855af7a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/contextswitch_p.h @@ -0,0 +1,56 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_CONTEXTSWITCH_P_H +#define KSYNTAXHIGHLIGHTING_CONTEXTSWITCH_P_H + +#include <QString> + +namespace KSyntaxHighlighting { + +class Context; +class Definition; + +class ContextSwitch +{ +public: + ContextSwitch() = default; + ~ContextSwitch() = default; + + bool isStay() const; + + int popCount() const; + Context* context() const; + + void parse(const QStringRef &contextInstr); + void resolve(const Definition &def); + +private: + QString m_defName; + QString m_contextName; + Context *m_context = nullptr; + int m_popCount = 0; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_CONTEXTSWITCH_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp new file mode 100644 index 0000000000..c03d23dc48 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp @@ -0,0 +1,798 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2018 Dominik Haumann <dhaumann@kde.org> + Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + + +#include "definition.h" +#include "definition_p.h" +#include "definitionref_p.h" + +#include "context_p.h" +#include "format.h" +#include "format_p.h" +#include "repository_p.h" +#include "rule_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "ksyntaxhighlighting_version.h" +#include "xml_p.h" + +#include <QCoreApplication> +#include <QDebug> +#include <QFile> +#include <QHash> +#include <QJsonObject> +#include <QStringList> +#include <QVector> +#include <QXmlStreamReader> + +#include <algorithm> + +using namespace KSyntaxHighlighting; + +DefinitionData::DefinitionData() + : wordDelimiters(QStringLiteral("\t !%&()*+,-./:;<=>?[\\]^{|}~")) // must be sorted! + , wordWrapDelimiters(wordDelimiters) +{ +} + +DefinitionData::~DefinitionData() +{ + qDeleteAll(contexts); +} + +DefinitionData* DefinitionData::get(const Definition &def) +{ + return def.d.get(); +} + +Definition::Definition() : + d(new DefinitionData) +{ +} + +Definition::Definition(const Definition &other) : + d(other.d) +{ + d->q = *this; +} + +Definition::Definition(const std::shared_ptr<DefinitionData> &dd) : + d(dd) +{ +} + +Definition::~Definition() +{ +} + +Definition& Definition::operator=(const Definition &rhs) +{ + d = rhs.d; + return *this; +} + +bool Definition::operator==(const Definition &other) const +{ + return d->fileName == other.d->fileName; +} + +bool Definition::operator!=(const Definition& other) const +{ + return d->fileName != other.d->fileName; +} + +bool Definition::isValid() const +{ + return d->repo && !d->fileName.isEmpty() && !d->name.isEmpty(); +} + +QString Definition::filePath() const +{ + return d->fileName; +} + +QString Definition::name() const +{ + return d->name; +} + +QString Definition::translatedName() const +{ + return QCoreApplication::instance()->translate("Language", d->name.toUtf8().constData()); +} + +QString Definition::section() const +{ + return d->section; +} + +QString Definition::translatedSection() const +{ + return QCoreApplication::instance()->translate("Language Section", d->section.toUtf8().constData()); +} + +QVector<QString> Definition::mimeTypes() const +{ + return d->mimetypes; +} + +QVector<QString> Definition::extensions() const +{ + return d->extensions; +} + +int Definition::version() const +{ + return d->version; +} + +int Definition::priority() const +{ + return d->priority; +} + +bool Definition::isHidden() const +{ + return d->hidden; +} + +QString Definition::style() const +{ + return d->style; +} + +QString Definition::indenter() const +{ + return d->indenter; +} + +QString Definition::author() const +{ + return d->author; +} + +QString Definition::license() const +{ + return d->license; +} + +bool Definition::isWordDelimiter(QChar c) const +{ + d->load(); + return d->isWordDelimiter(c); +} + +bool Definition::isWordWrapDelimiter(QChar c) const +{ + d->load(); + return std::binary_search(d->wordWrapDelimiters.constBegin(), d->wordWrapDelimiters.constEnd(), c); +} + +bool Definition::foldingEnabled() const +{ + d->load(); + if (d->hasFoldingRegions || indentationBasedFoldingEnabled()) { + return true; + } + + // check included definitions + const auto defs = includedDefinitions(); + for (const auto &def : defs) { + if (def.foldingEnabled()) { + d->hasFoldingRegions = true; + break; + } + } + + return d->hasFoldingRegions; +} + +bool Definition::indentationBasedFoldingEnabled() const +{ + d->load(); + return d->indentationBasedFolding; +} + +QStringList Definition::foldingIgnoreList() const +{ + d->load(); + return d->foldingIgnoreList; +} + +QStringList Definition::keywordLists() const +{ + d->load(); + return d->keywordLists.keys(); +} + +QStringList Definition::keywordList(const QString& name) const +{ + d->load(); + const auto list = d->keywordList(name); + return list ? list->keywords() : QStringList(); +} + +QVector<Format> Definition::formats() const +{ + d->load(); + + // sort formats so that the order matches the order of the itemDatas in the xml files. + auto formatList = QVector<Format>::fromList(d->formats.values()); + std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format & lhs, const KSyntaxHighlighting::Format & rhs){ + return lhs.id() < rhs.id(); + }); + + return formatList; +} + +QVector<Definition> Definition::includedDefinitions() const +{ + d->load(); + + // init worklist and result used as guard with this definition + QVector<Definition> queue{*this}; + QVector<Definition> definitions{*this}; + while (!queue.isEmpty()) { + // Iterate all context rules to find associated Definitions. This will + // automatically catch other Definitions referenced with IncludeRuldes or ContextSwitch. + const auto definition = queue.takeLast(); + for (const auto & context : qAsConst(definition.d->contexts)) { + // handle context switch attributes of this context itself + for (const auto switchContext : {context->lineEndContext().context(), context->lineEmptyContext().context(), context->fallthroughContext().context()}) { + if (switchContext) { + if (!definitions.contains(switchContext->definition())) { + queue.push_back(switchContext->definition()); + definitions.push_back(switchContext->definition()); + } + } + } + + // handle the embedded rules + for (const auto &rule : context->rules()) { + // handle include rules like inclusion + if (!definitions.contains(rule->definition())) { + queue.push_back(rule->definition()); + definitions.push_back(rule->definition()); + } + + // handle context switch context inclusion + if (auto switchContext = rule->context().context()) { + if (!definitions.contains(switchContext->definition())) { + queue.push_back(switchContext->definition()); + definitions.push_back(switchContext->definition()); + } + } + } + } + } + + // remove the 1st entry, since it is this Definition + definitions.pop_front(); + + return definitions; +} + +QString Definition::singleLineCommentMarker() const +{ + d->load(); + return d->singleLineCommentMarker; +} + +CommentPosition Definition::singleLineCommentPosition() const +{ + d->load(); + return d->singleLineCommentPosition; +} + +QPair<QString, QString> Definition::multiLineCommentMarker() const +{ + d->load(); + return { d->multiLineCommentStartMarker, d->multiLineCommentEndMarker }; +} + +QVector<QPair<QChar, QString>> Definition::characterEncodings() const +{ + d->load(); + return d->characterEncodings; +} + +Context* DefinitionData::initialContext() const +{ + Q_ASSERT(!contexts.isEmpty()); + return contexts.first(); +} + +Context* DefinitionData::contextByName(const QString& name) const +{ + foreach (auto context, contexts) { + if (context->name() == name) + return context; + } + return nullptr; +} + +KeywordList *DefinitionData::keywordList(const QString& name) +{ + auto it = keywordLists.find(name); + return (it == keywordLists.end()) ? nullptr : &it.value(); +} + +bool DefinitionData::isWordDelimiter(QChar c) const +{ + return std::binary_search(wordDelimiters.constBegin(), wordDelimiters.constEnd(), c); +} + +Format DefinitionData::formatByName(const QString& name) const +{ + const auto it = formats.constFind(name); + if (it != formats.constEnd()) + return it.value(); + + return Format(); +} + +bool DefinitionData::isLoaded() const +{ + return !contexts.isEmpty(); +} + +bool DefinitionData::load() +{ + if (fileName.isEmpty()) + return false; + + if (isLoaded()) + return true; + + QFile file(fileName); + if (!file.open(QFile::ReadOnly)) + return false; + + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + const auto token = reader.readNext(); + if (token != QXmlStreamReader::StartElement) + continue; + + if (reader.name() == QLatin1String("highlighting")) + loadHighlighting(reader); + + else if (reader.name() == QLatin1String("general")) + loadGeneral(reader); + } + + for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) + (*it).setCaseSensitivity(caseSensitive); + + foreach (auto context, contexts) { + context->resolveContexts(); + context->resolveIncludes(); + context->resolveAttributeFormat(); + } + + Q_ASSERT(std::is_sorted(wordDelimiters.constBegin(), wordDelimiters.constEnd())); + return true; +} + +void DefinitionData::clear() +{ + // keep only name and repo, so we can re-lookup to make references persist over repo reloads + keywordLists.clear(); + qDeleteAll(contexts); + contexts.clear(); + formats.clear(); + + fileName.clear(); + section.clear(); + style.clear(); + indenter.clear(); + author.clear(); + license.clear(); + mimetypes.clear(); + extensions.clear(); + wordDelimiters = QStringLiteral("\t !%&()*+,-./:;<=>?[\\]^{|}~"); // must be sorted! + wordWrapDelimiters = wordDelimiters; + caseSensitive = Qt::CaseSensitive; + version = 0.0f; + priority = 0; + hidden = false; +} + +bool DefinitionData::loadMetaData(const QString& definitionFileName) +{ + fileName = definitionFileName; + + QFile file(definitionFileName); + if (!file.open(QFile::ReadOnly)) + return false; + + QXmlStreamReader reader(&file); + while (!reader.atEnd()) { + const auto token = reader.readNext(); + if (token != QXmlStreamReader::StartElement) + continue; + if (reader.name() == QLatin1String("language")) { + return loadLanguage(reader); + } + } + + return false; +} + +bool DefinitionData::loadMetaData(const QString &file, const QJsonObject &obj) +{ + name = obj.value(QLatin1String("name")).toString(); + section = obj.value(QLatin1String("section")).toString(); + version = obj.value(QLatin1String("version")).toInt(); + priority = obj.value(QLatin1String("priority")).toInt(); + style = obj.value(QLatin1String("style")).toString(); + author = obj.value(QLatin1String("author")).toString(); + license = obj.value(QLatin1String("license")).toString(); + indenter = obj.value(QLatin1String("indenter")).toString(); + hidden = obj.value(QLatin1String("hidden")).toBool(); + fileName = file; + + const auto exts = obj.value(QLatin1String("extensions")).toString(); + foreach (const auto &ext, exts.split(QLatin1Char(';'), QString::SkipEmptyParts)) + extensions.push_back(ext); + const auto mts = obj.value(QLatin1String("mimetype")).toString(); + foreach (const auto &mt, mts.split(QLatin1Char(';'), QString::SkipEmptyParts)) + mimetypes.push_back(mt); + + return true; +} + +bool DefinitionData::loadLanguage(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("language")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + if (!checkKateVersion(reader.attributes().value(QStringLiteral("kateversion")))) + return false; + + name = reader.attributes().value(QStringLiteral("name")).toString(); + section = reader.attributes().value(QStringLiteral("section")).toString(); + // toFloat instead of toInt for backward compatibility with old Kate files + version = reader.attributes().value(QStringLiteral("version")).toFloat(); + priority = reader.attributes().value(QStringLiteral("priority")).toInt(); + hidden = Xml::attrToBool(reader.attributes().value(QStringLiteral("hidden"))); + style = reader.attributes().value(QStringLiteral("style")).toString(); + indenter = reader.attributes().value(QStringLiteral("indenter")).toString(); + author = reader.attributes().value(QStringLiteral("author")).toString(); + license = reader.attributes().value(QStringLiteral("license")).toString(); + const auto exts = reader.attributes().value(QStringLiteral("extensions")).toString(); + foreach (const auto &ext, exts.split(QLatin1Char(';'), QString::SkipEmptyParts)) + extensions.push_back(ext); + const auto mts = reader.attributes().value(QStringLiteral("mimetype")).toString(); + foreach (const auto &mt, mts.split(QLatin1Char(';'), QString::SkipEmptyParts)) + mimetypes.push_back(mt); + if (reader.attributes().hasAttribute(QStringLiteral("casesensitive"))) + caseSensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive; + return true; +} + +void DefinitionData::loadHighlighting(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("highlighting")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QLatin1String("list")) { + KeywordList keywords; + keywords.load(reader); + keywordLists.insert(keywords.name(), keywords); + } else if (reader.name() == QLatin1String("contexts")) { + loadContexts(reader); + reader.readNext(); + } else if (reader.name() == QLatin1String("itemDatas")) { + loadItemData(reader); + } else { + reader.readNext(); + } + break; + case QXmlStreamReader::EndElement: + return; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadContexts(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("contexts")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QLatin1String("context")) { + auto context = new Context; + context->setDefinition(q); + context->load(reader); + contexts.push_back(context); + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + return; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadItemData(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("itemDatas")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QLatin1String("itemData")) { + Format f; + auto formatData = FormatPrivate::detachAndGet(f); + formatData->definition = q; + formatData->load(reader); + formatData->id = RepositoryPrivate::get(repo)->nextFormatId(); + formats.insert(f.name(), f); + reader.readNext(); + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + return; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadGeneral(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("general")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + reader.readNext(); + + // reference counter to count XML child elements, to not return too early + int elementRefCounter = 1; + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + ++elementRefCounter; + + if (reader.name() == QLatin1String("keywords")) { + if (reader.attributes().hasAttribute(QStringLiteral("casesensitive"))) + caseSensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("casesensitive"))) ? Qt::CaseSensitive : Qt::CaseInsensitive; + + // adapt sorted wordDelimiters + wordDelimiters += reader.attributes().value(QStringLiteral("additionalDeliminator")); + std::sort(wordDelimiters.begin(), wordDelimiters.end()); + auto it = std::unique(wordDelimiters.begin(), wordDelimiters.end()); + wordDelimiters.truncate(std::distance(wordDelimiters.begin(), it)); + foreach (const auto c, reader.attributes().value(QLatin1String("weakDeliminator"))) + wordDelimiters.remove(c); + + // adaptWordWrapDelimiters, and sort + wordWrapDelimiters = reader.attributes().value(QStringLiteral("wordWrapDeliminator")).toString(); + std::sort(wordWrapDelimiters.begin(), wordWrapDelimiters.end()); + if (wordWrapDelimiters.isEmpty()) + wordWrapDelimiters = wordDelimiters; + } else if (reader.name() == QLatin1String("folding")) { + if (reader.attributes().hasAttribute(QStringLiteral("indentationsensitive"))) + indentationBasedFolding = Xml::attrToBool(reader.attributes().value(QStringLiteral("indentationsensitive"))); + } else if (reader.name() == QLatin1String("emptyLines")) { + loadFoldingIgnoreList(reader); + } else if (reader.name() == QLatin1String("comments")) { + loadComments(reader); + } else if (reader.name() == QLatin1String("spellchecking")) { + loadSpellchecking(reader); + } else { + reader.skipCurrentElement(); + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + --elementRefCounter; + if (elementRefCounter == 0) + return; + reader.readNext(); + break; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadComments(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("comments")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + reader.readNext(); + + // reference counter to count XML child elements, to not return too early + int elementRefCounter = 1; + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + ++elementRefCounter; + if (reader.name() == QLatin1String("comment")) { + const bool isSingleLine = reader.attributes().value(QStringLiteral("name")) == QStringLiteral("singleLine"); + if (isSingleLine) { + singleLineCommentMarker = reader.attributes().value(QStringLiteral("start")).toString(); + const bool afterWhiteSpace = reader.attributes().value(QStringLiteral("position")).toString() == QStringLiteral("afterwhitespace"); + singleLineCommentPosition = afterWhiteSpace ? CommentPosition::AfterWhitespace : CommentPosition::StartOfLine; + } else { + multiLineCommentStartMarker = reader.attributes().value(QStringLiteral("start")).toString(); + multiLineCommentEndMarker = reader.attributes().value(QStringLiteral("end")).toString(); + } + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + --elementRefCounter; + if (elementRefCounter == 0) + return; + reader.readNext(); + break; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadFoldingIgnoreList(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("emptyLines")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + reader.readNext(); + + // reference counter to count XML child elements, to not return too early + int elementRefCounter = 1; + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + ++elementRefCounter; + if (reader.name() == QLatin1String("emptyLine")) { + foldingIgnoreList << reader.attributes().value(QStringLiteral("regexpr")).toString(); + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + --elementRefCounter; + if (elementRefCounter == 0) + return; + reader.readNext(); + break; + default: + reader.readNext(); + break; + } + } +} + +void DefinitionData::loadSpellchecking(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.name() == QLatin1String("spellchecking")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + reader.readNext(); + + // reference counter to count XML child elements, to not return too early + int elementRefCounter = 1; + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + ++elementRefCounter; + if (reader.name() == QLatin1String("encoding")) { + const auto charRef = reader.attributes().value(QStringLiteral("char")); + if (!charRef.isEmpty()) { + const auto str = reader.attributes().value(QStringLiteral("string")).toString(); + characterEncodings.push_back({ charRef[0], str }); + } + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + --elementRefCounter; + if (elementRefCounter == 0) + return; + reader.readNext(); + break; + default: + reader.readNext(); + break; + } + } +} + +bool DefinitionData::checkKateVersion(const QStringRef& verStr) +{ + const auto idx = verStr.indexOf(QLatin1Char('.')); + if (idx <= 0) { + qCWarning(Log) << "Skipping" << fileName << "due to having no valid kateversion attribute:" << verStr; + return false; + } + const auto major = verStr.left(idx).toInt(); + const auto minor = verStr.mid(idx + 1).toInt(); + + if (major > SyntaxHighlighting_VERSION_MAJOR || (major == SyntaxHighlighting_VERSION_MAJOR && minor > SyntaxHighlighting_VERSION_MINOR)) { + qCWarning(Log) << "Skipping" << fileName << "due to being too new, version:" << verStr; + return false; + } + + return true; +} + +quint16 DefinitionData::foldingRegionId(const QString &foldName) +{ + hasFoldingRegions = true; + return RepositoryPrivate::get(repo)->foldingRegionId(name, foldName); +} + +DefinitionRef::DefinitionRef() +{ +} + +DefinitionRef::DefinitionRef(const Definition &def) : + d(def.d) +{ +} + +DefinitionRef::~DefinitionRef() +{ +} + +DefinitionRef& DefinitionRef::operator=(const Definition &def) +{ + d = def.d; + return *this; +} + +Definition DefinitionRef::definition() const +{ + if (!d.expired()) + return Definition(d.lock()); + return Definition(); +} + +bool DefinitionRef::operator==(const DefinitionRef &other) const +{ + if (d.expired() != other.d.expired()) { + return false; + } + + return d.expired() || d.lock().get() == other.d.lock().get(); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h new file mode 100644 index 0000000000..6f0dba9a45 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h @@ -0,0 +1,400 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_DEFINITION_H +#define KSYNTAXHIGHLIGHTING_DEFINITION_H + +#include "ksyntaxhighlighting_export.h" + +#include <QTypeInfo> +#include <QPair> + +#include <memory> + +QT_BEGIN_NAMESPACE +class QChar; +class QString; +class QStringList; +template <typename T> class QVector; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class Context; +class Format; +class KeywordList; + +class DefinitionData; + +/** + * Defines the insert position when commenting code. + * @since 5.50 + * @see Definition::singleLineCommentPosition() + */ +enum class CommentPosition +{ + //! The comment marker is inserted at the beginning of a line at column 0 + StartOfLine = 0, + //! The comment marker is inserted after leading whitespaces right befire + //! the first non-whitespace character. + AfterWhitespace +}; + +/** + * Represents a syntax definition. + * + * @section def_intro Introduction to Definitions + * + * A Definition is the short term for a syntax highlighting definition. It + * typically is defined in terms of an XML syntax highlighting file, containing + * all information about a particular syntax highlighting. This includes the + * highlighting of keywords, information about code folding regions, and + * indentation preferences. + * + * @section def_info General Header Data + * + * Each Definition contains a non-translated unique name() and a section(). + * In addition, for putting this information e.g. into menus, the functions + * translatedName() and translatedSection() are provided. However, if isHidden() + * returns @e true, the Definition should not be visible in the UI. The location + * of the Definition can be obtained through filePath(), which either is the + * location on disk or a path to a compiled-in Qt resource. + * + * The supported files of a Definition are defined by the list of extensions(), + * and additionally by the list of mimeTypes(). Note, that extensions() returns + * wildcards that need to be matched against the filename of the file that + * requires highlighting. If multiple Definition%s match the file, then the one + * with higher priority() wins. + * + * @section def_metadata Advanced Definition Data + * + * Advanced text editors such as Kate require additional information from a + * Definition. For instance, foldingEnabled() defines whether a Definition has + * code folding regions that can be shown in a code folding pane. Or + * singleLineCommentMarker() and multiLineCommentMarker() provide comment + * markers that can be used for commenting/uncommenting code. Similarly, + * formats() returns a list of Format items defined by this Definition (which + * equal the itemDatas of a highlighing definition file). includedDefinitions() + * returns a list of all included Definition%s referenced by this Definition via + * the rule IncludeRules, which is useful for displaying all Format items for + * color configuration in the user interface. + * + * @see Repository + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT Definition +{ +public: + /** + * Default constructor, creating an empty (invalid) Definition instance. + * isValid() for this instance returns @e false. + * + * Use the Repository instead to obtain valid instances. + */ + Definition(); + + /** + * Copy constructor. + * Both this definition as well as @p other share the Definition data. + */ + Definition(const Definition &other); + + /** + * Destructor. + */ + ~Definition(); + + /** + * Assignment operator. + * Both this definition as well as @p rhs share the Definition data. + */ + Definition& operator=(const Definition &rhs); + + /** + * Checks two definitions for equality. + */ + bool operator==(const Definition &other) const; + + /** + * Checks two definitions for inequality. + */ + bool operator!=(const Definition &other) const; + + /** + * @name General Header Data + * + * @{ + */ + + /** + * Checks whether this object refers to a valid syntax definition. + */ + bool isValid() const; + + /** + * Returns the full path to the definition XML file containing + * the syntax definition. Note that this can be a path to QRC content. + */ + QString filePath() const; + + /** Name of the syntax. + * Used for internal references, prefer translatedName() for display. + */ + QString name() const; + + /** + * Translated name for display. + */ + QString translatedName() const; + + /** + * The group this syntax definition belongs to. + * For display, consider translatedSection(). + */ + QString section() const; + + /** + * Translated group name for display. + */ + QString translatedSection() const; + + /** + * Mime types associated with this syntax definition. + */ + QVector<QString> mimeTypes() const; + + /** + * File extensions associated with this syntax definition. + * The returned list contains wildcards. + */ + QVector<QString> extensions() const; + + /** + * Returns the definition version. + */ + int version() const; + + /** + * Returns the definition priority. + * A Definition with higher priority wins over Definitions with lower priorities. + */ + int priority() const; + + /** + * Returns @c true if this is an internal definition that should not be + * displayed to the user. + */ + bool isHidden() const; + + /** + * Generalized language style, used for indentation. + */ + QString style() const; + + /** + * Indentation style to be used for this syntax. + */ + QString indenter() const; + + /** + * Name and email of the author of this syntax definition. + */ + QString author() const; + + /** + * License of this syntax definition. + */ + QString license() const; + + /** + * @} + * + * @name Advanced Definition Data + */ + + /** + * Returns whether the character @p c is a word delimiter. + * A delimiter defines whether a characters is a word boundary. Internally, + * delimiters are used for matching keyword lists. As example, typically the + * dot '.' is a word delimiter. However, if you have a keyword in a keyword + * list that contains a dot, you have to add the dot to the + * @e weakDeliminator attribute of the @e general section in your + * highlighting definition. Similarly, sometimes additional delimiters are + * required, which can be specified in @e additionalDeliminator. + * + * Checking whether a character is a delimiter is useful for instance if + * text is selected with double click. Typically, the whole word should be + * selected in this case. Similarly to the example above, the dot '.' + * usually acts as word delimiter. However, using this function you can + * implement text selection in such a way that keyword lists are correctly + * selected. + * + * @note By default, the list of delimiters contains the following + * characters: \\t !%&()*+,-./:;<=>?[\\]^{|}~ + * + * @since 5.50 + * @see isWordWrapDelimiter() + */ + bool isWordDelimiter(QChar c) const; + + /** + * Returns whether it is safe to break a line at before the character @c. + * This is useful when wrapping a line e.g. by applying static word wrap. + * + * As example, consider the LaTeX code + * @code + * \command1\command2 + * @endcode + * Applying static word wrap could lead to the following code: + * @code + * \command1\ + * command2 + * @endcode + * command2 without a leading backslash is invalid in LaTeX. If '\\' is set + * as word wrap delimiter, isWordWrapDelimiter('\\') then returns true, + * meaning that it is safe to break the line before @c. The resulting code + * then would be + * @code + * \command1 + * \command2 + * @endcode + * + * @note By default, the word wrap delimiters are equal to the word + * delimiters in isWordDelimiter(). + * + * @since 5.50 + * @see isWordDelimiter() + */ + bool isWordWrapDelimiter(QChar c) const; + + /** + * Returns whether the highlighting supports code folding. + * Code folding is supported either if the highlighting defines code folding + * regions or if indentationBasedFoldingEnabled() returns @e true. + * @since 5.50 + * @see indentationBasedFoldingEnabled() + */ + bool foldingEnabled() const; + + /** + * Returns whether indentation-based folding is enabled. + * An example for indentation-based folding is Python. + * When indentation-based folding is enabled, make sure to also check + * foldingIgnoreList() for lines that should be treated as empty. + * + * @see foldingIgnoreList(), State::indentationBasedFoldingEnabled() + */ + bool indentationBasedFoldingEnabled() const; + + /** + * If indentationBasedFoldingEnabled() returns @c true, this function returns + * a list of regular expressions that represent empty lines. That is, all + * lines matching entirely one of the regular expressions should be treated + * as empty lines when calculating the indentation-based folding ranges. + * + * @note This list is only of relevance, if indentationBasedFoldingEnabled() + * returns @c true. + * + * @see indentationBasedFoldingEnabled() + */ + QStringList foldingIgnoreList() const; + + /** + * Returns the section names of keywords. + * @since 5.49 + * @see keywordList() + */ + QStringList keywordLists() const; + + /** + * Returns the list of keywords for the keyword list @p name. + * @since 5.49 + * @see keywordLists() + */ + QStringList keywordList(const QString& name) const; + + /** + * Returns a list of all Format items used by this definition. + * The order of the Format items equals the order of the itemDatas in the xml file. + * @since 5.49 + */ + QVector<Format> formats() const; + + /** + * Returns a list of Definitions that are referenced with the IncludeRules rule. + * The returned list does not include this Definition. In case no other + * Definitions are referenced via IncludeRules, the returned list is empty. + * + * @since 5.49 + */ + QVector<Definition> includedDefinitions() const; + + /** + * Returns the marker that starts a single line comment. + * For instance, in C++ the single line comment marker is "//". + * @since 5.50 + * @see singleLineCommentPosition(); + */ + QString singleLineCommentMarker() const; + + /** + * Returns the insert position of the comment marker for sinle line + * comments. + * @since 5.50 + * @see singleLineCommentMarker(); + */ + CommentPosition singleLineCommentPosition() const; + + /** + * Returns the markers that start and end multiline comments. + * For instance, in XML this is defined as "<!--" and "-->". + * @since 5.50 + */ + QPair<QString, QString> multiLineCommentMarker() const; + + /** + * Returns a list of character/string mapping that can be used for spell + * checking. This is useful for instance when spell checking LaTeX, where + * the string \"{A} represents the character Ä. + * @since 5.50 + */ + QVector<QPair<QChar, QString>> characterEncodings() const; + + /** + * @} + */ + +private: + friend class DefinitionData; + friend class DefinitionRef; + explicit Definition(const std::shared_ptr<DefinitionData> &dd); + std::shared_ptr<DefinitionData> d; +}; + +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h new file mode 100644 index 0000000000..ab95a9552c --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h @@ -0,0 +1,111 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_DEFINITION_P_H +#define KSYNTAXHIGHLIGHTING_DEFINITION_P_H + +#include "definitionref_p.h" +#include "definition.h" + +#include <QHash> +#include <QString> +#include <QVector> + +QT_BEGIN_NAMESPACE +class QXmlStreamReader; +class QJsonObject; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class Repository; + +class DefinitionData +{ +public: + DefinitionData(); + ~DefinitionData(); + + static DefinitionData* get(const Definition &def); + + bool isLoaded() const; + bool loadMetaData(const QString &definitionFileName); + bool loadMetaData(const QString &fileName, const QJsonObject &obj); + + void clear(); + + bool load(); + bool loadLanguage(QXmlStreamReader &reader); + void loadHighlighting(QXmlStreamReader &reader); + void loadContexts(QXmlStreamReader &reader); + void loadItemData(QXmlStreamReader &reader); + void loadGeneral(QXmlStreamReader &reader); + void loadComments(QXmlStreamReader &reader); + void loadFoldingIgnoreList(QXmlStreamReader &reader); + void loadSpellchecking(QXmlStreamReader &reader); + bool checkKateVersion(const QStringRef &verStr); + + KeywordList *keywordList(const QString &name); + bool isWordDelimiter(QChar c) const; + + Context* initialContext() const; + Context* contextByName(const QString &name) const; + + Format formatByName(const QString &name) const; + + quint16 foldingRegionId(const QString &foldName); + + DefinitionRef q; + + Repository *repo = nullptr; + QHash<QString, KeywordList> keywordLists; + QVector<Context*> contexts; + QHash<QString, Format> formats; + QString wordDelimiters; + QString wordWrapDelimiters; + bool hasFoldingRegions = false; + bool indentationBasedFolding = false; + QStringList foldingIgnoreList; + QString singleLineCommentMarker; + CommentPosition singleLineCommentPosition = CommentPosition::StartOfLine; + QString multiLineCommentStartMarker; + QString multiLineCommentEndMarker; + QVector<QPair<QChar, QString>> characterEncodings; + + QString fileName; + QString name = QStringLiteral(QT_TRANSLATE_NOOP("Syntax highlighting", "None")); + QString section; + QString style; + QString indenter; + QString author; + QString license; + QVector<QString> mimetypes; + QVector<QString> extensions; + Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive; + int version = 0; + int priority = 0; + bool hidden = false; +}; +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp new file mode 100644 index 0000000000..4c3e5f5f1e --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp @@ -0,0 +1,193 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "definitiondownloader.h" +#include "definition.h" +#include "repository.h" +#include "ksyntaxhighlighting_logging.h" +#include "ksyntaxhighlighting_version.h" + +#include <QDebug> +#include <QDir> +#include <QFile> +#include <QNetworkAccessManager> +#include <QNetworkReply> +#include <QNetworkRequest> +#include <QStandardPaths> +#include <QTimer> +#include <QXmlStreamReader> + +using namespace KSyntaxHighlighting; + +class KSyntaxHighlighting::DefinitionDownloaderPrivate +{ +public: + DefinitionDownloader *q; + Repository *repo; + QNetworkAccessManager *nam; + QString downloadLocation; + int pendingDownloads; + bool needsReload; + + void definitionListDownloadFinished(QNetworkReply *reply); + void updateDefinition(QXmlStreamReader &parser); + void downloadDefinition(const QUrl &url); + void downloadDefinitionFinished(QNetworkReply *reply); + void checkDone(); +}; + +void DefinitionDownloaderPrivate::definitionListDownloadFinished(QNetworkReply *reply) +{ + if (reply->error() != QNetworkReply::NoError) { + qCWarning(Log) << reply->error(); + emit q->done(); // TODO return error + return; + } + + QXmlStreamReader parser(reply); + while (!parser.atEnd()) { + switch (parser.readNext()) { + case QXmlStreamReader::StartElement: + if (parser.name() == QLatin1String("Definition")) + updateDefinition(parser); + break; + default: + break; + } + } + + if (pendingDownloads == 0) + emit q->informationMessage(QObject::tr("All syntax definitions are up-to-date.")); + checkDone(); +} + +void DefinitionDownloaderPrivate::updateDefinition(QXmlStreamReader &parser) +{ + const auto name = parser.attributes().value(QLatin1String("name")); + if (name.isEmpty()) + return; + + auto localDef = repo->definitionForName(name.toString()); + if (!localDef.isValid()) { + emit q->informationMessage(QObject::tr("Downloading new syntax definition for '%1'...").arg(name.toString())); + downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString())); + return; + } + + const auto version = parser.attributes().value(QLatin1String("version")); + if (localDef.version() < version.toFloat()) { + emit q->informationMessage(QObject::tr("Updating syntax definition for '%1' to version %2...").arg(name.toString(), version.toString())); + downloadDefinition(QUrl(parser.attributes().value(QLatin1String("url")).toString())); + } +} + +void DefinitionDownloaderPrivate::downloadDefinition(const QUrl& downloadUrl) +{ + if (!downloadUrl.isValid()) + return; + auto url = downloadUrl; + if (url.scheme() == QLatin1String("http")) + url.setScheme(QStringLiteral("https")); + + QNetworkRequest req(url); + auto reply = nam->get(req); + QObject::connect(reply, &QNetworkReply::finished, q, [this, reply]() { + downloadDefinitionFinished(reply); + }); + ++pendingDownloads; + needsReload = true; +} + +void DefinitionDownloaderPrivate::downloadDefinitionFinished(QNetworkReply *reply) +{ + --pendingDownloads; + if (reply->error() != QNetworkReply::NoError) { + qCWarning(Log) << "Failed to download definition file" << reply->url() << reply->error(); + checkDone(); + return; + } + + // handle redirects + // needs to be done manually, download server redirects to unsafe http links + const auto redirectUrl = reply->attribute(QNetworkRequest::RedirectionTargetAttribute).toUrl(); + if (!redirectUrl.isEmpty()) { + downloadDefinition(reply->url().resolved(redirectUrl)); + checkDone(); + return; + } + + QFile file(downloadLocation + QLatin1Char('/') + reply->url().fileName()); + if (!file.open(QFile::WriteOnly)) { + qCWarning(Log) << "Failed to open" << file.fileName() << file.error(); + } else { + file.write(reply->readAll()); + } + checkDone(); +} + +void DefinitionDownloaderPrivate::checkDone() +{ + if (pendingDownloads == 0) { + if (needsReload) + repo->reload(); + + emit QTimer::singleShot(0, q, &DefinitionDownloader::done); + } +} + + +DefinitionDownloader::DefinitionDownloader(Repository *repo, QObject *parent) + : QObject(parent) + , d(new DefinitionDownloaderPrivate()) +{ + Q_ASSERT(repo); + + d->q = this; + d->repo = repo; + d->nam = new QNetworkAccessManager(this); + d->pendingDownloads = 0; + d->needsReload = false; + + d->downloadLocation = QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation) + QStringLiteral("/org.kde.syntax-highlighting/syntax"); + QDir().mkpath(d->downloadLocation); + Q_ASSERT(QFile::exists(d->downloadLocation)); +} + +DefinitionDownloader::~DefinitionDownloader() +{ +} + +void DefinitionDownloader::start() +{ + const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-") + + QString::number(SyntaxHighlighting_VERSION_MAJOR) + + QLatin1Char('.') + + QString::number(SyntaxHighlighting_VERSION_MINOR) + + QLatin1String(".xml"); + auto req = QNetworkRequest(QUrl(url)); + req.setAttribute(QNetworkRequest::FollowRedirectsAttribute, true); + auto reply = d->nam->get(req); + QObject::connect(reply, &QNetworkReply::finished, this, [=]() { + d->definitionListDownloadFinished(reply); + }); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h new file mode 100644 index 0000000000..06e28f6a65 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_DEFINITIONDOWNLOADER_H +#define KSYNTAXHIGHLIGHTING_DEFINITIONDOWNLOADER_H + +#include "ksyntaxhighlighting_export.h" + +#include <QObject> +#include <memory> + +namespace KSyntaxHighlighting { + +class DefinitionDownloaderPrivate; +class Repository; + +/** + * Helper class to download definition file updates. + * + * With the DefinitionDownloader you can download new and update existing + * syntax highlighting definition files (xml files). + * + * An example that updates the highlighting Definition%s and prints the current + * update progress to the console may look as follows: + * + * @code + * auto downloader = new DefinitionDownloader(repo); // repo is a pointer to a Repository + * + * // print update progress to console + * QObject::connect(downloader, &DefinitionDownloader::informationMessage, [](const QString &msg) { + * std::cout << qPrintable(msg) << std::endl; + * }); + * + * // connect to signal done to delete the downloader later + * QObject::connect(downloader, &DefinitionDownloader::done, + * downloader, &DefinitionDownloader::deleteLater); + * downloader->start(); + * @endcode + * + * @see Repository, Definition + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT DefinitionDownloader : public QObject +{ + Q_OBJECT +public: + /** + * Constructor. + * The Repository @p repo is used as reference to compare the versions of + * the existing Definition%s with the ones that are available online. + * + * Optionally, @p parent is a pointer to the owner of this instance. + */ + explicit DefinitionDownloader(Repository *repo, QObject *parent = nullptr); + + /** + * Destructor. + */ + ~DefinitionDownloader(); + + /** + * Starts the update procedure. + * Once no more updates are available (i.e. either the local definition files + * are up-to-date, or all updates have been downloaded), the signal done() + * is emitted. + * + * During the update process, the signal informationMessage() can be used + * to display the current update progress to the user. + * + * @see done(), informationMessage() + */ + void start(); + +Q_SIGNALS: + /** + * Prints the information about the current state of the definition files. + * If all files are up-to-date, this signal is emitted informing you that + * all highlighting files are up-to-date. If there are updates, this signal + * is emitted for each update being downloaded. + */ + void informationMessage(const QString &msg); + + /** + * This signal is emitted when there are no pending downloads anymore. + */ + void done(); + +private: + std::unique_ptr<DefinitionDownloaderPrivate> d; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_DEFINITIONDOWNLOADER_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h new file mode 100644 index 0000000000..08604a4821 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h @@ -0,0 +1,74 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_DEFINITIONREF_P_H +#define KSYNTAXHIGHLIGHTING_DEFINITIONREF_P_H + +#include <memory> + +namespace KSyntaxHighlighting { + +class Definition; +class DefinitionData; +class DefinitionPrivate; + +/** Weak reference for Definition instances. + * + * This must be used when holding Definition instances + * in objects hold directly or indirectly by Definition + * to avoid reference count loops and thus memory leaks. + * + * @internal + */ +class DefinitionRef +{ +public: + DefinitionRef(); + explicit DefinitionRef(const Definition &def); + ~DefinitionRef(); + DefinitionRef& operator=(const Definition &def); + + Definition definition() const; + + /** + * Checks two definition references for equality. + */ + bool operator==(const DefinitionRef &other) const; + + /** + * Checks two definition references for inequality. + */ + bool operator!=(const DefinitionRef &other) const + { + return !(*this == other); + } + +private: + friend class DefinitionData; + std::weak_ptr<DefinitionData> d; +}; + +} + +#endif + diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp new file mode 100644 index 0000000000..e8f89bd788 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp @@ -0,0 +1,60 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "foldingregion.h" + +using namespace KSyntaxHighlighting; + +static_assert(sizeof(FoldingRegion) == 2, "FoldingRegion is size-sensitive to frequent use in KTextEditor!"); + +FoldingRegion::FoldingRegion() : + m_type(None), + m_id(0) +{ +} + +FoldingRegion::FoldingRegion(Type type, quint16 id) : + m_type(type), + m_id(id) +{ +} + +bool FoldingRegion::operator==(const FoldingRegion &other) const +{ + return m_id == other.m_id && m_type == other.m_type; +} + +bool FoldingRegion::isValid() const +{ + return type() != None; +} + +quint16 FoldingRegion::id() const +{ + return m_id; +} + +FoldingRegion::Type FoldingRegion::type() const +{ + return static_cast<FoldingRegion::Type>(m_type); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h new file mode 100644 index 0000000000..074b9478be --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h @@ -0,0 +1,108 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_FOLDINGREGION_H +#define KSYNTAXHIGHLIGHTING_FOLDINGREGION_H + +#include "ksyntaxhighlighting_export.h" + +#include <QTypeInfo> + +namespace KSyntaxHighlighting { + +/** Represents a begin or end of a folding region. + * @since 5.28 */ +class KSYNTAXHIGHLIGHTING_EXPORT FoldingRegion +{ +public: + /** + * Defines whether a FoldingRegion starts or ends a folding region. + */ + enum Type { + //! Used internally as indicator for invalid FoldingRegion%s. + None, + //! Indicates the start of a FoldingRegion. + Begin, + //! Indicates the end of a FoldingRegion. + End + }; + + /** + * Constructs an invalid folding region, meaning that isValid() returns @e false. + * To obtain valid instances, see AbstractHighlighter::applyFolding(). + */ + FoldingRegion(); + + /** Compares two FoldingRegion instances for equality. */ + bool operator==(const FoldingRegion &other) const; + + /** + * Returns @c true if this is a valid folding region. + * A valid FoldingRegion is defined by a type() other than Type::None. + * + * @note The FoldingRegion%s passed in AbstractHighlighter::applyFolding() + * are always valid. + */ + bool isValid() const; + + /** + * Returns a unique identifier for this folding region. + * + * As example, the C/C++ highlighter starts and ends a folding region for + * scopes, e.g.: + * \code + * void foo() { // '{' starts a folding region + * if (bar()) { // '{' starts a (nested) folding region + * } // '}' ends the (nested) folding region + * } // '}' ends the outer folding region + * \endcode + * In this example, all braces '{' and '}' have the same id(), meaning that + * if you want to find the matching closing region for the first opening + * brace, you need to do kind of a reference counting to find the correct + * closing brace. + */ + quint16 id() const; + + /** + * Returns whether this is the begin or end of a region. + * + * @note The FoldingRegion%s passed in AbstractHighlighter::applyFolding() + * are always valid, i.e. either Type::Begin or Type::End. + */ + Type type() const; + +private: + friend class Rule; + FoldingRegion(Type type, quint16 id); + + quint16 m_type : 2; + quint16 m_id: 14; +}; + +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::FoldingRegion, Q_PRIMITIVE_TYPE); +QT_END_NAMESPACE + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp new file mode 100644 index 0000000000..397da6d700 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp @@ -0,0 +1,264 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "format.h" +#include "format_p.h" +#include "definition.h" +#include "definitionref_p.h" +#include "textstyledata_p.h" +#include "themedata_p.h" +#include "xml_p.h" + +#include <QColor> +#include <QDebug> +#include <QMetaEnum> +#include <QXmlStreamReader> + +using namespace KSyntaxHighlighting; + +static Theme::TextStyle stringToDefaultFormat(const QStringRef &str) +{ + if (!str.startsWith(QLatin1String("ds"))) + return Theme::Normal; + + static const auto idx = Theme::staticMetaObject.indexOfEnumerator("TextStyle"); + Q_ASSERT(idx >= 0); + const auto metaEnum = Theme::staticMetaObject.enumerator(idx); + + bool ok = false; + const auto value = metaEnum.keyToValue(str.mid(2).toLatin1().constData(), &ok); + if (!ok || value < 0) + return Theme::Normal; + return static_cast<Theme::TextStyle>(value); +} + +FormatPrivate* FormatPrivate::detachAndGet(Format &format) +{ + format.d.detach(); + return format.d.data(); +} + +TextStyleData FormatPrivate::styleOverride(const Theme &theme) const +{ + const auto themeData = ThemeData::get(theme); + if (themeData) + return themeData->textStyleOverride(definition.definition().name(), name); + return TextStyleData(); +} + +static QExplicitlySharedDataPointer<FormatPrivate> &sharedDefaultPrivate() +{ + static QExplicitlySharedDataPointer<FormatPrivate> def(new FormatPrivate); + return def; +} + +Format::Format() : d(sharedDefaultPrivate()) +{ +} + +Format::Format(const Format &other) : + d(other.d) +{ +} + +Format::~Format() +{ +} + +Format& Format::operator=(const Format& other) +{ + d = other.d; + return *this; +} + +bool Format::isValid() const +{ + return !d->name.isEmpty(); +} + +QString Format::name() const +{ + return d->name; +} + +quint16 Format::id() const +{ + return d->id; +} + +Theme::TextStyle Format::textStyle() const +{ + return d->defaultStyle; +} + +bool Format::isDefaultTextStyle(const Theme &theme) const +{ + return (!hasTextColor(theme)) + && (!hasBackgroundColor(theme)) + && (selectedTextColor(theme) == theme.selectedTextColor(Theme::Normal)) + && (selectedBackgroundColor(theme) == theme.selectedBackgroundColor(Theme::Normal)) + && (isBold(theme) == theme.isBold(Theme::Normal)) + && (isItalic(theme) == theme.isItalic(Theme::Normal)) + && (isUnderline(theme) == theme.isUnderline(Theme::Normal)) + && (isStrikeThrough(theme) == theme.isStrikeThrough(Theme::Normal)); +} + +bool Format::hasTextColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + return textColor(theme) != theme.textColor(Theme::Normal) + && (d->style.textColor || theme.textColor(d->defaultStyle) || overrideStyle.textColor); +} + +QColor Format::textColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.textColor) + return overrideStyle.textColor; + return d->style.textColor ? d->style.textColor : theme.textColor(d->defaultStyle); +} + +QColor Format::selectedTextColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.selectedTextColor) + return overrideStyle.selectedTextColor; + return d->style.selectedTextColor ? d->style.selectedTextColor : theme.selectedTextColor(d->defaultStyle); +} + +bool Format::hasBackgroundColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + return backgroundColor(theme) != theme.backgroundColor(Theme::Normal) + && (d->style.backgroundColor || theme.backgroundColor(d->defaultStyle) || overrideStyle.backgroundColor); +} + +QColor Format::backgroundColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.backgroundColor) + return overrideStyle.backgroundColor; + return d->style.backgroundColor ? d->style.backgroundColor : theme.backgroundColor(d->defaultStyle); +} + +QColor Format::selectedBackgroundColor(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.selectedBackgroundColor) + return overrideStyle.selectedBackgroundColor; + return d->style.selectedBackgroundColor ? d->style.selectedBackgroundColor + : theme.selectedBackgroundColor(d->defaultStyle); +} + +bool Format::isBold(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.hasBold) + return overrideStyle.bold; + return d->style.hasBold ? d->style.bold : theme.isBold(d->defaultStyle); +} + +bool Format::isItalic(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.hasItalic) + return overrideStyle.italic; + return d->style.hasItalic ? d->style.italic : theme.isItalic(d->defaultStyle); +} + +bool Format::isUnderline(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.hasUnderline) + return overrideStyle.underline; + return d->style.hasUnderline ? d->style.underline : theme.isUnderline(d->defaultStyle); +} + +bool Format::isStrikeThrough(const Theme &theme) const +{ + const auto overrideStyle = d->styleOverride(theme); + if (overrideStyle.hasStrikeThrough) + return overrideStyle.strikeThrough; + return d->style.hasStrikeThrough ? d->style.strikeThrough : theme.isStrikeThrough(d->defaultStyle); +} + +bool Format::spellCheck() const +{ + return d->spellCheck; +} + + +void FormatPrivate::load(QXmlStreamReader& reader) +{ + name = reader.attributes().value(QStringLiteral("name")).toString(); + defaultStyle = stringToDefaultFormat(reader.attributes().value(QStringLiteral("defStyleNum"))); + + QStringRef ref = reader.attributes().value(QStringLiteral("color")); + if (!ref.isEmpty()) { + style.textColor = QColor(ref.toString()).rgba(); + } + + ref = reader.attributes().value(QStringLiteral("selColor")); + if (!ref.isEmpty()) { + style.selectedTextColor = QColor(ref.toString()).rgba(); + } + + ref = reader.attributes().value(QStringLiteral("backgroundColor")); + if (!ref.isEmpty()) { + style.backgroundColor = QColor(ref.toString()).rgba(); + } + + ref = reader.attributes().value(QStringLiteral("selBackgroundColor")); + if (!ref.isEmpty()) { + style.selectedBackgroundColor = QColor(ref.toString()).rgba(); + } + + ref = reader.attributes().value(QStringLiteral("italic")); + if (!ref.isEmpty()) { + style.hasItalic = true; + style.italic = Xml::attrToBool(ref); + } + + ref = reader.attributes().value(QStringLiteral("bold")); + if (!ref.isEmpty()) { + style.hasBold = true; + style.bold = Xml::attrToBool(ref); + } + + ref = reader.attributes().value(QStringLiteral("underline")); + if (!ref.isEmpty()) { + style.hasUnderline = true; + style.underline = Xml::attrToBool(ref); + } + + ref = reader.attributes().value(QStringLiteral("strikeOut")); + if (!ref.isEmpty()) { + style.hasStrikeThrough = true; + style.strikeThrough = Xml::attrToBool(ref); + } + + ref = reader.attributes().value(QStringLiteral("spellChecking")); + if (!ref.isEmpty()) { + spellCheck = Xml::attrToBool(ref); + } +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.h b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h new file mode 100644 index 0000000000..24c58e73f6 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h @@ -0,0 +1,152 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_FORMAT_H +#define KSYNTAXHIGHLIGHTING_FORMAT_H + +#include "ksyntaxhighlighting_export.h" +#include "theme.h" + +#include <QExplicitlySharedDataPointer> +#include <QTypeInfo> + +QT_BEGIN_NAMESPACE +class QColor; +class QString; +class QXmlStreamReader; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class DefinitionRef; +class FormatPrivate; + +/** Describes the format to be used for a specific text fragment. + * The actual format used for displaying is merged from the format information + * in the syntax definition file, and a theme. + * + * @see Theme + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT Format +{ +public: + /** Creates an empty/invalid format. */ + Format(); + Format(const Format &other); + ~Format(); + + Format& operator=(const Format &other); + + /** Returns @c true if this is a valid format, ie. one that + * was read from a syntax definition file. + */ + bool isValid() const; + + /** The name of this format as used in the syntax definition file. */ + QString name() const; + + /** Returns a unique identifier of this format. + * This is useful for efficient storing of formats in a text line. The + * identifier is unique per Repository instance, but will change when + * the repository is reloaded (which also invalidatess the corresponding + * Definition anyway). + */ + quint16 id() const; + + /** Returns the underlying TextStyle of this Format. + * Every Theme::TextStyle is visually defined by a Theme. A Format uses one + * of the Theme::TextStyle%s and on top allows modifications such as setting + * a different foreground color etc. + * @see Theme::TextStyle + * @since 5.49 + */ + Theme::TextStyle textStyle() const; + + /** Returns @c true if the combination of this format and the theme @p theme + * do not change the default text format in any way. + * This is useful for output formats where changing formatting implies cost, + * and thus benefit from optimizing the default case of not having any format + * applied. If you make use of this, make sure to set the default text style + * to what the corresponding theme sets for Theme::Normal. + */ + bool isDefaultTextStyle(const Theme &theme) const; + + /** Returns @c true if the combination of this format and the theme @p theme + * change the foreground color compared to the default format. + */ + bool hasTextColor(const Theme &theme) const; + /** Returns the foreground color of the combination of this format and the + * given theme. + */ + QColor textColor(const Theme &theme) const; + /** Returns the foreground color for selected text of the combination of + * this format and the given theme. + */ + QColor selectedTextColor(const Theme &theme) const; + /** Returns @c true if the combination of this format and the theme @p theme + * change the background color compared to the default format. + */ + bool hasBackgroundColor(const Theme &theme) const; + /** Returns the background color of the combination of this format and the + * given theme. + */ + QColor backgroundColor(const Theme &theme) const; + /** Returns the background color of selected text of the combination of + * this format and the given theme. + */ + QColor selectedBackgroundColor(const Theme &theme) const; + + /** Returns @c true if the combination of this format and the given theme + * results in bold text formatting. + */ + bool isBold(const Theme &theme) const; + /** Returns @c true if the combination of this format and the given theme + * results in italic text formatting. + */ + bool isItalic(const Theme &theme) const; + /** Returns @c true if the combination of this format and the given theme + * results in underlined text. + */ + bool isUnderline(const Theme &theme) const; + /** Returns @c true if the combination of this format and the given theme + * results in struck through text. + */ + bool isStrikeThrough(const Theme &theme) const; + + /** + * Returns whether characters with this format should be spell checked. + */ + bool spellCheck() const; + +private: + friend class FormatPrivate; + QExplicitlySharedDataPointer<FormatPrivate> d; +}; +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // KSYNTAXHIGHLIGHTING_FORMAT_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h new file mode 100644 index 0000000000..e79b26b6a7 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h @@ -0,0 +1,55 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_FORMAT_P_H +#define KSYNTAXHIGHLIGHTING_FORMAT_P_H + +#include "definitionref_p.h" +#include "textstyledata_p.h" +#include "theme.h" + +#include <QSharedData> +#include <QString> + +namespace KSyntaxHighlighting { + +class FormatPrivate : public QSharedData +{ +public: + FormatPrivate() = default; + static FormatPrivate* detachAndGet(Format &format); + + TextStyleData styleOverride(const Theme &theme) const; + void load(QXmlStreamReader &reader); + + DefinitionRef definition; + QString name; + TextStyleData style; + Theme::TextStyle defaultStyle = Theme::Normal; + quint16 id = 0; + bool spellCheck = true; +}; + +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp new file mode 100644 index 0000000000..4ebd465b77 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp @@ -0,0 +1,161 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "htmlhighlighter.h" +#include "definition.h" +#include "format.h" +#include "state.h" +#include "theme.h" +#include "ksyntaxhighlighting_logging.h" + +#include <QDebug> +#include <QFile> +#include <QFileInfo> +#include <QTextStream> +#include <QVarLengthArray> + +using namespace KSyntaxHighlighting; + +class KSyntaxHighlighting::HtmlHighlighterPrivate +{ +public: + std::unique_ptr<QTextStream> out; + std::unique_ptr<QFile> file; + QString currentLine; +}; + +HtmlHighlighter::HtmlHighlighter() + : d(new HtmlHighlighterPrivate()) +{ +} + +HtmlHighlighter::~HtmlHighlighter() +{ +} + +void HtmlHighlighter::setOutputFile(const QString& fileName) +{ + d->file.reset(new QFile(fileName)); + if (!d->file->open(QFile::WriteOnly | QFile::Truncate)) { + qCWarning(Log) << "Failed to open output file" << fileName << ":" << d->file->errorString(); + return; + } + d->out.reset(new QTextStream(d->file.get())); + d->out->setCodec("UTF-8"); +} + +void HtmlHighlighter::setOutputFile(FILE *fileHandle) +{ + d->out.reset(new QTextStream(fileHandle, QIODevice::WriteOnly)); + d->out->setCodec("UTF-8"); +} + +void HtmlHighlighter::highlightFile(const QString& fileName, const QString& title) +{ + QFileInfo fi(fileName); + QFile f(fileName); + if (!f.open(QFile::ReadOnly)) { + qCWarning(Log) << "Failed to open input file" << fileName << ":" << f.errorString(); + return; + } + + if (title.isEmpty()) + highlightData(&f, fi.fileName()); + else + highlightData(&f, title); +} + +void HtmlHighlighter::highlightData(QIODevice *dev, const QString& title) +{ + if (!d->out) { + qCWarning(Log) << "No output stream defined!"; + return; + } + + QString htmlTitle; + if (title.isEmpty()) + htmlTitle = QStringLiteral("Kate Syntax Highlighter"); + else + htmlTitle = title.toHtmlEscaped(); + + State state; + *d->out << "<!DOCTYPE html>\n"; + *d->out << "<html><head>\n"; + *d->out << "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\"/>\n"; + *d->out << "<title>" << htmlTitle << "</title>\n"; + *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting (" << definition().name() << ")\"/>\n"; + *d->out << "</head><body"; + if (theme().textColor(Theme::Normal)) + *d->out << " style=\"color:" << QColor(theme().textColor(Theme::Normal)).name() << "\""; + *d->out << "><pre>\n"; + + QTextStream in(dev); + in.setCodec("UTF-8"); + while (!in.atEnd()) { + d->currentLine = in.readLine(); + state = highlightLine(d->currentLine, state); + *d->out << "\n"; + } + + *d->out << "</pre></body></html>\n"; + d->out->flush(); + + d->out.reset(); + d->file.reset(); +} + +void HtmlHighlighter::applyFormat(int offset, int length, const Format& format) +{ + if (length == 0) + return; + + // collect potential output, cheaper than thinking about "is there any?" + QVarLengthArray<QString, 16> formatOutput; + if (format.hasTextColor(theme())) + formatOutput << QStringLiteral("color:") << format.textColor(theme()).name() << QStringLiteral(";"); + if (format.hasBackgroundColor(theme())) + formatOutput << QStringLiteral("background-color:") << format.backgroundColor(theme()).name() << QStringLiteral(";"); + if (format.isBold(theme())) + formatOutput << QStringLiteral("font-weight:bold;"); + if (format.isItalic(theme())) + formatOutput << QStringLiteral("font-style:italic;"); + if (format.isUnderline(theme())) + formatOutput << QStringLiteral("text-decoration:underline;"); + if (format.isStrikeThrough(theme())) + formatOutput << QStringLiteral("text-decoration:line-through;"); + + if (!formatOutput.isEmpty()) { + *d->out << "<span style=\""; + for (const auto &out : qAsConst(formatOutput)) { + *d->out << out; + } + *d->out << "\">"; + } + + *d->out << d->currentLine.mid(offset, length).toHtmlEscaped(); + + if (!formatOutput.isEmpty()) { + *d->out << "</span>"; + } +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h new file mode 100644 index 0000000000..b7eda02d54 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h @@ -0,0 +1,64 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_HTMLHIGHLIGHTER_H +#define KSYNTAXHIGHLIGHTING_HTMLHIGHLIGHTER_H + +#include "ksyntaxhighlighting_export.h" +#include "abstracthighlighter.h" + +#include <QString> +#include <QIODevice> + +#include <memory> + +QT_BEGIN_NAMESPACE +class QFile; +class QTextStream; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class HtmlHighlighterPrivate; + +class KSYNTAXHIGHLIGHTING_EXPORT HtmlHighlighter : public AbstractHighlighter +{ +public: + HtmlHighlighter(); + ~HtmlHighlighter() override; + + void highlightFile(const QString &fileName, const QString &title = QString()); + void highlightData(QIODevice *device, const QString &title = QString()); + + void setOutputFile(const QString &fileName); + void setOutputFile(FILE *fileHandle); + +protected: + void applyFormat(int offset, int length, const Format &format) override; + +private: + std::unique_ptr<HtmlHighlighterPrivate> d; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_HTMLHIGHLIGHTER_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp new file mode 100644 index 0000000000..fe5f77586a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp @@ -0,0 +1,104 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "keywordlist_p.h" + +#include <QDebug> +#include <QXmlStreamReader> + +#include <algorithm> + +using namespace KSyntaxHighlighting; + +bool KeywordList::contains(const QStringRef &str, Qt::CaseSensitivity caseSensitive) const +{ + /** + * get right vector to search in + */ + const auto &vectorToSearch = (caseSensitive == Qt::CaseSensitive) ? m_keywordsSortedCaseSensitive : m_keywordsSortedCaseInsensitive; + + /** + * search with right predicate + */ + return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), str, [caseSensitive] (const QStringRef &a, const QStringRef &b) { return a.compare(b, caseSensitive) < 0; }); +} + +void KeywordList::load(QXmlStreamReader& reader) +{ + Q_ASSERT(reader.name() == QLatin1String("list")); + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + m_name = reader.attributes().value(QStringLiteral("name")).toString(); + + while (!reader.atEnd()) { + switch (reader.tokenType()) { + case QXmlStreamReader::StartElement: + if (reader.name() == QLatin1String("item")) { + m_keywords.append(reader.readElementText().trimmed()); + reader.readNextStartElement(); + break; + } + reader.readNext(); + break; + case QXmlStreamReader::EndElement: + reader.readNext(); + return; + default: + reader.readNext(); + break; + } + } +} + +void KeywordList::setCaseSensitivity(Qt::CaseSensitivity caseSensitive) +{ + /** + * remember default case-sensitivity and init lookup for it + */ + m_caseSensitive = caseSensitive; + initLookupForCaseSensitivity(m_caseSensitive); +} + +void KeywordList::initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive) +{ + /** + * get right vector to sort, if non-empty, we are done + */ + auto &vectorToSort = (caseSensitive == Qt::CaseSensitive) ? m_keywordsSortedCaseSensitive : m_keywordsSortedCaseInsensitive; + if (!vectorToSort.empty()) { + return; + } + + /** + * fill vector with refs to keywords + */ + vectorToSort.reserve(m_keywords.size()); + for (const auto &keyword : qAsConst(m_keywords)) { + vectorToSort.push_back(&keyword); + } + + /** + * sort with right predicate + */ + std::sort(vectorToSort.begin(), vectorToSort.end(), [caseSensitive] (const QStringRef &a, const QStringRef &b) { return a.compare(b, caseSensitive) < 0; }); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h new file mode 100644 index 0000000000..8c41aabe0c --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist_p.h @@ -0,0 +1,101 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_KEYWORDLIST_P_H +#define KSYNTAXHIGHLIGHTING_KEYWORDLIST_P_H + +#include <QSet> +#include <QString> +#include <QVector> + +#include <vector> + +QT_BEGIN_NAMESPACE +class QXmlStreamReader; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class KeywordList +{ +public: + KeywordList() = default; + ~KeywordList() = default; + + bool isEmpty() const + { + return m_keywords.isEmpty(); + } + + const QString &name() const + { + return m_name; + } + + const QStringList &keywords() const + { + return m_keywords; + } + + /** Checks if @p str is a keyword in this list. */ + bool contains(const QStringRef &str) const + { + return contains(str, m_caseSensitive); + } + + /** Checks if @p str is a keyword in this list, overriding the global case-sensitivity setting. */ + bool contains(const QStringRef &str, Qt::CaseSensitivity caseSensitive) const; + + void load(QXmlStreamReader &reader); + void setCaseSensitivity(Qt::CaseSensitivity caseSensitive); + void initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive); + +private: + /** + * name of keyword list as in XML + */ + QString m_name; + + /** + * raw list of keywords, as seen in XML (but trimmed) + */ + QStringList m_keywords; + + /** + * default case-sensitivity setting + */ + Qt::CaseSensitivity m_caseSensitive = Qt::CaseSensitive; + + /** + * case-sensitive sorted string references to m_keywords for lookup + */ + std::vector<QStringRef> m_keywordsSortedCaseSensitive; + + /** + * case-insensitive sorted string references to m_keywords for lookup + */ + std::vector<QStringRef> m_keywordsSortedCaseInsensitive; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_KEYWORDLIST_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h b/src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h new file mode 100644 index 0000000000..a39adb5ed6 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/ksyntaxhighlighting_export.h @@ -0,0 +1,34 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QtGlobal> + +#if defined(KSYNTAXHIGHLIGHTING_LIBRARY) +# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_EXPORT +#else +# define KSYNTAXHIGHLIGHTING_EXPORT Q_DECL_IMPORT +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h new file mode 100644 index 0000000000..b1a05ee636 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h @@ -0,0 +1,113 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_MATCHRESULT_P_H +#define KSYNTAXHIGHLIGHTING_MATCHRESULT_P_H + +#include <QStringList> + +namespace KSyntaxHighlighting { + +/** + * Storage for match result of a Rule. + * Heavily used internally during highlightLine, therefore completely inline. + */ +class MatchResult +{ +public: + /** + * Match at given offset found. + * @param offset offset of match + */ + MatchResult(const int offset) + : m_offset(offset) + { + } + + /** + * Match at given offset found with additional skip offset. + */ + explicit MatchResult(const int offset, const int skipOffset) + : m_offset(offset) + , m_skipOffset(skipOffset) + { + } + + /** + * Match at given offset found with additional captures. + * @param offset offset of match + * @param captures captures of the match + */ + explicit MatchResult(const int offset, const QStringList &captures) + : m_offset(offset) + , m_captures(captures) + { + } + + /** + * Offset of the match + * @return offset of the match + */ + int offset() const + { + return m_offset; + } + + + /** + * Skip offset of the match + * @return skip offset of the match, no match possible until this offset is reached + */ + int skipOffset() const + { + return m_skipOffset; + } + + /** + * Captures of the match. + * @return captured text of this match + */ + const QStringList &captures() const + { + return m_captures; + } + +private: + /** + * match offset, filled in all constructors + */ + int m_offset; + + /** + * skip offset, optional + */ + int m_skipOffset = 0; + + /** + * captures, optional + */ + QStringList m_captures; +}; +} + +#endif // KSYNTAXHIGHLIGHTING_MATCHRESULT_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp new file mode 100644 index 0000000000..6b2fabd07a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp @@ -0,0 +1,325 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "repository.h" +#include "repository_p.h" +#include "definition.h" +#include "definition_p.h" +#include "theme.h" +#include "themedata_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "wildcardmatcher_p.h" + +#include <QDebug> +#include <QDirIterator> +#include <QFile> +#include <QFileInfo> +#include <QJsonDocument> +#include <QJsonObject> + +#ifndef NO_STANDARD_PATHS +#include <QStandardPaths> +#endif + +#include <limits> + +using namespace KSyntaxHighlighting; + +static void initResource() +{ +#ifdef HAS_SYNTAX_RESOURCE + Q_INIT_RESOURCE(syntax_data); +#endif + Q_INIT_RESOURCE(theme_data); +} + +RepositoryPrivate* RepositoryPrivate::get(Repository *repo) +{ + return repo->d.get(); +} + +Repository::Repository() : + d(new RepositoryPrivate) +{ + initResource(); + d->load(this); +} + +Repository::~Repository() +{ + // reset repo so we can detect in still alive definition instances + // that the repo was deleted + foreach (const auto &def, d->m_sortedDefs) + DefinitionData::get(def)->repo = nullptr; +} + +Definition Repository::definitionForName(const QString& defName) const +{ + return d->m_defs.value(defName); +} + +static Definition bestCandidate(QVector<Definition>& candidates) +{ + if (candidates.isEmpty()) + return Definition(); + + std::partial_sort(candidates.begin(), candidates.begin() + 1, candidates.end(), [](const Definition &lhs, const Definition &rhs) { + return lhs.priority() > rhs.priority(); + }); + + return candidates.at(0); +} + +Definition Repository::definitionForFileName(const QString& fileName) const +{ + QFileInfo fi(fileName); + const auto name = fi.fileName(); + + QVector<Definition> candidates; + for (auto it = d->m_defs.constBegin(); it != d->m_defs.constEnd(); ++it) { + auto def = it.value(); + foreach (const auto &pattern, def.extensions()) { + if (WildcardMatcher::exactMatch(name, pattern)) { + candidates.push_back(def); + break; + } + } + } + + return bestCandidate(candidates); +} + +Definition Repository::definitionForMimeType(const QString& mimeType) const +{ + QVector<Definition> candidates; + for (auto it = d->m_defs.constBegin(); it != d->m_defs.constEnd(); ++it) { + auto def = it.value(); + foreach (const auto &matchType, def.mimeTypes()) { + if (mimeType == matchType) { + candidates.push_back(def); + break; + } + } + } + + return bestCandidate(candidates); +} + +QVector<Definition> Repository::definitions() const +{ + return d->m_sortedDefs; +} + +QVector<Theme> Repository::themes() const +{ + return d->m_themes; +} + +Theme Repository::theme(const QString &themeName) const +{ + for (const auto &theme : qAsConst(d->m_themes)) { + if (theme.name() == themeName) { + return theme; + } + } + + return Theme(); +} + +Theme Repository::defaultTheme(Repository::DefaultTheme t) +{ + if (t == DarkTheme) + return theme(QLatin1String("Breeze Dark")); + return theme(QLatin1String("Default")); +} + +void RepositoryPrivate::load(Repository *repo) +{ + // always add invalid default "None" highlighting + addDefinition(Definition()); + + // do lookup in standard paths, if not disabled +#ifndef NO_STANDARD_PATHS + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/syntax"), QStandardPaths::LocateDirectory)) + loadSyntaxFolder(repo, dir); + + // backward compatibility with Kate + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory)) + loadSyntaxFolder(repo, dir); +#endif + + // default resources are always used + loadSyntaxFolder(repo, QStringLiteral(":/org.kde.syntax-highlighting/syntax")); + + // user given extra paths + foreach (const auto &path, m_customSearchPaths) + loadSyntaxFolder(repo, path + QStringLiteral("/syntax")); + + m_sortedDefs.reserve(m_defs.size()); + for (auto it = m_defs.constBegin(); it != m_defs.constEnd(); ++it) + m_sortedDefs.push_back(it.value()); + std::sort(m_sortedDefs.begin(), m_sortedDefs.end(), [](const Definition &left, const Definition &right) { + auto comparison = left.translatedSection().compare(right.translatedSection(), Qt::CaseInsensitive); + if (comparison == 0) + comparison = left.translatedName().compare(right.translatedName(), Qt::CaseInsensitive); + return comparison < 0; + }); + + // load themes + + // do lookup in standard paths, if not disabled +#ifndef NO_STANDARD_PATHS + foreach (const auto &dir, QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/themes"), QStandardPaths::LocateDirectory)) + loadThemeFolder(dir); +#endif + + // default resources are always used + loadThemeFolder(QStringLiteral(":/org.kde.syntax-highlighting/themes")); + + // user given extra paths + foreach (const auto &path, m_customSearchPaths) + loadThemeFolder(path + QStringLiteral("/themes")); +} + +void RepositoryPrivate::loadSyntaxFolder(Repository *repo, const QString &path) +{ + if (loadSyntaxFolderFromIndex(repo, path)) + return; + + QDirIterator it(path, QStringList() << QLatin1String("*.xml"), QDir::Files); + while (it.hasNext()) { + Definition def; + auto defData = DefinitionData::get(def); + defData->repo = repo; + if (defData->loadMetaData(it.next())) + addDefinition(def); + } +} + +bool RepositoryPrivate::loadSyntaxFolderFromIndex(Repository *repo, const QString &path) +{ + QFile indexFile(path + QLatin1String("/index.katesyntax")); + if (!indexFile.open(QFile::ReadOnly)) + return false; + + const auto indexDoc(QJsonDocument::fromBinaryData(indexFile.readAll())); + const auto index = indexDoc.object(); + for (auto it = index.begin(); it != index.end(); ++it) { + if (!it.value().isObject()) + continue; + const auto fileName = QString(path + QLatin1Char('/') + it.key()); + const auto defMap = it.value().toObject(); + Definition def; + auto defData = DefinitionData::get(def); + defData->repo = repo; + if (defData->loadMetaData(fileName, defMap)) + addDefinition(def); + } + return true; +} + +void RepositoryPrivate::addDefinition(const Definition &def) +{ + const auto it = m_defs.constFind(def.name()); + if (it == m_defs.constEnd()) { + m_defs.insert(def.name(), def); + return; + } + + if (it.value().version() >= def.version()) + return; + m_defs.insert(def.name(), def); +} + +void RepositoryPrivate::loadThemeFolder(const QString &path) +{ + QDirIterator it(path, QStringList() << QLatin1String("*.theme"), QDir::Files); + while (it.hasNext()) { + auto themeData = std::unique_ptr<ThemeData>(new ThemeData); + if (themeData->load(it.next())) + addTheme(Theme(themeData.release())); + } +} + +static int themeRevision(const Theme &theme) +{ + auto data = ThemeData::get(theme); + return data->revision(); +} + +void RepositoryPrivate::addTheme(const Theme &theme) +{ + const auto it = std::lower_bound(m_themes.begin(), m_themes.end(), theme, [](const Theme &lhs, const Theme &rhs) { + return lhs.name() < rhs.name(); + }); + if (it == m_themes.end() || (*it).name() != theme.name()) { + m_themes.insert(it, theme); + return; + } + if (themeRevision(*it) < themeRevision(theme)) + *it = theme; +} + +quint16 RepositoryPrivate::foldingRegionId(const QString &defName, const QString &foldName) +{ + const auto it = m_foldingRegionIds.constFind(qMakePair(defName, foldName)); + if (it != m_foldingRegionIds.constEnd()) + return it.value(); + m_foldingRegionIds.insert(qMakePair(defName, foldName), ++m_foldingRegionId); + return m_foldingRegionId; +} + +quint16 RepositoryPrivate::nextFormatId() +{ + Q_ASSERT(m_formatId < std::numeric_limits<quint16>::max()); + return ++m_formatId; +} + +void Repository::reload() +{ + qCDebug(Log) << "Reloading syntax definitions!"; + foreach (const auto &def, d->m_sortedDefs) + DefinitionData::get(def)->clear(); + d->m_defs.clear(); + d->m_sortedDefs.clear(); + + d->m_themes.clear(); + + d->m_foldingRegionId = 0; + d->m_foldingRegionIds.clear(); + + d->m_formatId = 0; + + d->load(this); +} + +void Repository::addCustomSearchPath(const QString &path) +{ + d->m_customSearchPaths.append(path); + reload(); +} + +QVector<QString> Repository::customSearchPaths() const +{ + return d->m_customSearchPaths; +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h new file mode 100644 index 0000000000..c35da5ec37 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h @@ -0,0 +1,257 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_REPOSITORY_H +#define KSYNTAXHIGHLIGHTING_REPOSITORY_H + +#include "ksyntaxhighlighting_export.h" + +#include <qglobal.h> +#include <memory> + +QT_BEGIN_NAMESPACE +class QString; +template <typename T> class QVector; +QT_END_NAMESPACE + +/** + * @namespace KSyntaxHighlighting + * + * Syntax highlighting engine for Kate syntax definitions. + * In order to access the syntax highlighting Definition files, use the + * class Repository. + * + * @see Repository + */ +namespace KSyntaxHighlighting { + +class Definition; +class RepositoryPrivate; +class Theme; + +/** + * @brief Syntax highlighting repository. + * + * @section repo_intro Introduction + * + * The Repository gives access to all syntax Definitions available on the + * system, typically described in *.xml files. The Definition files are read + * from the resource that is compiled into the executable, and from the file + * system. If a Definition exists in the resource and on the file system, + * then the one from the file system is chosen. + * + * @section repo_access Definitions and Themes + * + * Typically, only one instance of the Repository is needed. This single + * instance can be thought of as a singleton you keep alive throughout the + * lifetime of your application. Then, either call definitionForName() with the + * given language name (e.g. "QML" or "Java"), or definitionForFileName() to + * obtain a Definition based on the filename/mimetype of the file. The + * function definitions() returns a list of all available syntax Definition%s. + * + * In addition to Definitions, the Repository also provides a list of Themes. + * A Theme is defined by a set of default text style colors as well as editor + * colors. These colors together provide all required colros for drawing all + * primitives of a text editor. All available Theme%s can be queried through + * themes(), and a Theme with a specific name is obtained through theme(). + * Additionally, defaultTheme() provides a way to obtain a default theme for + * either a light or a black color theme. + * + * @section repo_search_paths Search Paths + * + * All highlighting Definition and Theme files are compiled into the shared + * KSyntaxHighlighting library by using the Qt resource system. Loading + * additional files from disk is supported as well. + * + * Loading syntax Definition files works as follows: + * + * -# First, all syntax highlighting files are loaded that are located in + * QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/syntax"), QStandardPaths::LocateDirectory); + * Under Unix, this uses $XDG_DATA_HOME and $XDG_DATA_DIRS, which could + * map to $HOME/.local5/share/org.kde.syntax-highlighting/syntax and + * /usr/share/org.kde.syntax-highlighting/syntax. + * + * -# Next, for backwards compatibility with Kate, all syntax highlighting + * files are loaded that are located in + * QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory); + * Again, under Unix, this uses $XDG_DATA_HOME and $XDG_DATA_DIRS, which + * could map to $HOME/.local5/share/katepart5/syntax and + * /usr/share/katepart5/syntax. + * + * -# Then, all files compiled into the library through resources are loaded. + * The internal resource path is ":/org.kde.syntax-highlighting/syntax". + * This path should never be touched by other applications. + * + * -# Finally, the search path can be extended by calling addCustomSearchPath(). + * A custom search path can either be a path on disk or again a path to + * a Qt resource. + * + * Similarly, loading Theme files works as follows: + * + * -# First, all Theme files are loaded that are located in + * QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("org.kde.syntax-highlighting/themes"), QStandardPaths::LocateDirectory); + * Under Unix, this uses $XDG_DATA_HOME and $XDG_DATA_DIRS, which could + * map to $HOME/.local5/share/org.kde.syntax-highlighting/themes and + * /usr/share/org.kde.syntax-highlighting/themes. + * + * -# Then, all files compiled into the library through resources are loaded. + * The internal resource path is ":/org.kde.syntax-highlighting/themes". + * This path should never be touched by other applications. + * + * -# Finally, all Theme%s located in the paths added addCustomSearchPath() + * are loaded. + * + * @note Whenever a Definition or a Theme exists twice, the variant with + * higher version is used. + * + * @note The QStandardPaths lookup can be disabled by compiling the framework with the -DNO_STANDARD_PATHS define. + * + * @see Definition, Theme, AbstractHighlighter + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT Repository +{ +public: + /** + * Create a new syntax definition repository. + * This will read the meta data information of all available syntax + * definition, which is a moderately expensive operation, it's therefore + * recommended to keep a single instance of Repository around as long + * as you need highlighting in your application. + */ + Repository(); + ~Repository(); + + /** + * Returns the Definition named @p defName. + * + * If no Definition is found, Definition::isValid() of the returned instance + * returns false. + * + * @note This uses case sensitive, untranslated names. For instance, + * the javascript.xml definition file sets its name to @e JavaScript. + * Therefore, only the string "JavaScript" will return a valid + * Definition file. + */ + Definition definitionForName(const QString &defName) const; + + /** + * Returns the best matching Definition for the file named @p fileName. + * The match is performed based on the \e extensions and @e mimetype of + * the definition files. If multiple matches are found, the one with the + * highest priority is returned. + * + * If no match is found, Definition::isValid() of the returned instance + * returns false. + */ + Definition definitionForFileName(const QString &fileName) const; + + /** + * Returns the best matching Definition to the type named @p mimeType + * + * If no match is found, Definition::isValid() of the returned instance + * returns false. + * + * @since 5.50 + */ + Definition definitionForMimeType(const QString &mimeType) const; + + /** + * Returns all available Definition%s. + * Definition%ss are ordered by translated section and translated names, + * for consistent displaying. + */ + QVector<Definition> definitions() const; + + /** + * Returns all available color themes. + * The returned list should never be empty. + */ + QVector<Theme> themes() const; + + /** + * Returns the theme called @p themeName. + * If the requested theme cannot be found, the retunred Theme is invalid, + * see Theme::isValid(). + */ + Theme theme(const QString &themeName) const; + + /** + * Built-in default theme types. + * @see defaultTheme() + */ + enum DefaultTheme { + //! Theme with a light background color. + LightTheme, + //! Theme with a dark background color. + DarkTheme + }; + + /** + * Returns a default theme instance of the given type. + * The returned Theme is guaranteed to be a valid theme. + */ + Theme defaultTheme(DefaultTheme t = LightTheme); + + /** + * Reloads the repository. + * This is a moderately expensive operations and should thus only be + * triggered when the installed syntax definition files changed. + */ + void reload(); + + /** + * Add a custom search path to the repository. + * This path will be searched in addition to the usual locations for syntax + * and theme definition files. Both locations on disk as well as Qt + * resource paths are supported. + * + * @note Internally, the two sub-folders @p path/syntax as well as + * @p path/themes are searched for additional Definition%s and + * Theme%s. Do not append @e syntax or @e themes to @p path + * yourself. + * + * @note Calling this triggers a reload() of the repository. + * + * @since 5.39 + */ + void addCustomSearchPath(const QString &path); + + /** + * Returns the list of custom search paths added to the repository. + * By default, this list is empty. + * + * @see addCustomSearchPath() + * @since 5.39 + */ + QVector<QString> customSearchPaths() const; + +private: + Q_DISABLE_COPY(Repository) + friend class RepositoryPrivate; + std::unique_ptr<RepositoryPrivate> d; +}; + +} + +#endif // KSYNTAXHIGHLIGHTING_REPOSITORY_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h new file mode 100644 index 0000000000..9db876be59 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h @@ -0,0 +1,72 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_REPOSITORY_P_H +#define KSYNTAXHIGHLIGHTING_REPOSITORY_P_H + +#include <QHash> +#include <QVector> + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class Definition; +class Repository; +class Theme; + +class RepositoryPrivate +{ +public: + RepositoryPrivate() = default; + + static RepositoryPrivate* get(Repository *repo); + + void load(Repository *repo); + void loadSyntaxFolder(Repository *repo, const QString &path); + bool loadSyntaxFolderFromIndex(Repository *repo, const QString &path); + + void addDefinition(const Definition &def); + + void loadThemeFolder(const QString &path); + void addTheme(const Theme &theme); + + quint16 foldingRegionId(const QString &defName, const QString &foldName); + quint16 nextFormatId(); + + QVector<QString> m_customSearchPaths; + + QHash<QString, Definition> m_defs; + QVector<Definition> m_sortedDefs; + + QVector<Theme> m_themes; + + QHash<QPair<QString, QString>, quint16> m_foldingRegionIds; + quint16 m_foldingRegionId = 0; + quint16 m_formatId = 0; +}; +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp new file mode 100644 index 0000000000..c48753bf0c --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp @@ -0,0 +1,663 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "rule_p.h" +#include "context_p.h" +#include "definition_p.h" +#include "ksyntaxhighlighting_logging.h" +#include "xml_p.h" + +#include <QString> +#include <QXmlStreamReader> + +using namespace KSyntaxHighlighting; + +static bool isOctalChar(QChar c) +{ + return c.isNumber() && c != QLatin1Char('9') && c != QLatin1Char('8'); +} + +static bool isHexChar(QChar c) +{ + return c.isNumber() + || c == QLatin1Char('a') || c == QLatin1Char('A') + || c == QLatin1Char('b') || c == QLatin1Char('B') + || c == QLatin1Char('c') || c == QLatin1Char('C') + || c == QLatin1Char('d') || c == QLatin1Char('D') + || c == QLatin1Char('e') || c == QLatin1Char('E') + || c == QLatin1Char('f') || c == QLatin1Char('F'); +} + +static int matchEscapedChar(const QString &text, int offset) +{ + if (text.at(offset) != QLatin1Char('\\') || text.size() < offset + 2) + return offset; + + const auto c = text.at(offset + 1); + static const auto controlChars = QStringLiteral("abefnrtv\"'?\\"); + if (controlChars.contains(c)) + return offset + 2; + + if (c == QLatin1Char('x')) { // hex encoded character + auto newOffset = offset + 2; + for (int i = 0; i < 2 && newOffset + i < text.size(); ++i, ++newOffset) { + if (!isHexChar(text.at(newOffset))) + break; + } + if (newOffset == offset + 2) + return offset; + return newOffset; + } + + if (isOctalChar(c)) { // octal encoding + auto newOffset = offset + 2; + for (int i = 0; i < 2 && newOffset + i < text.size(); ++i, ++newOffset) { + if (!isOctalChar(text.at(newOffset))) + break; + } + if (newOffset == offset + 2) + return offset; + return newOffset; + } + + return offset; +} + +static QString replaceCaptures(const QString &pattern, const QStringList &captures, bool quote) +{ + auto result = pattern; + for (int i = captures.size() - 1; i >= 1; --i) { + result.replace(QLatin1Char('%') + QString::number(i), quote ? QRegularExpression::escape(captures.at(i)) : captures.at(i)); + } + return result; +} + +Definition Rule::definition() const +{ + return m_def.definition(); +} + +void Rule::setDefinition(const Definition &def) +{ + m_def = def; + + // cache for DefinitionData::wordDelimiters, is accessed VERY often + m_wordDelimiter = &DefinitionData::get(m_def.definition())->wordDelimiters; +} + +bool Rule::load(QXmlStreamReader &reader) +{ + Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + + m_attribute = reader.attributes().value(QStringLiteral("attribute")).toString(); + if (reader.name() != QLatin1String("IncludeRules")) // IncludeRules uses this with a different semantic + m_context.parse(reader.attributes().value(QStringLiteral("context"))); + m_firstNonSpace = Xml::attrToBool(reader.attributes().value(QStringLiteral("firstNonSpace"))); + m_lookAhead = Xml::attrToBool(reader.attributes().value(QStringLiteral("lookAhead"))); + bool colOk = false; + m_column = reader.attributes().value(QStringLiteral("column")).toInt(&colOk); + if (!colOk) + m_column = -1; + + auto regionName = reader.attributes().value(QLatin1String("beginRegion")); + if (!regionName.isEmpty()) + m_beginRegion = FoldingRegion(FoldingRegion::Begin, DefinitionData::get(m_def.definition())->foldingRegionId(regionName.toString())); + regionName = reader.attributes().value(QLatin1String("endRegion")); + if (!regionName.isEmpty()) + m_endRegion = FoldingRegion(FoldingRegion::End, DefinitionData::get(m_def.definition())->foldingRegionId(regionName.toString())); + + auto result = doLoad(reader); + + if (m_lookAhead && m_context.isStay()) + result = false; + + // be done with this rule, skip all subelements, e.g. no longer supported sub-rules + reader.skipCurrentElement(); + return result; +} + +void Rule::resolveContext() +{ + m_context.resolve(m_def.definition()); +} + +void Rule::resolveAttributeFormat(Context *lookupContext) +{ + /** + * try to get our format from the definition we stem from + */ + if (!m_attribute.isEmpty()) { + m_attributeFormat = DefinitionData::get(definition())->formatByName(m_attribute); + if (!m_attributeFormat.isValid()) { + qCWarning(Log) << "Rule: Unknown format" << m_attribute << "in context" << lookupContext->name() << "of definition" << definition().name(); + } + } +} + +bool Rule::doLoad(QXmlStreamReader& reader) +{ + Q_UNUSED(reader); + return true; +} + +Rule::Ptr Rule::create(const QStringRef& name) +{ + Rule *rule = nullptr; + if (name == QLatin1String("AnyChar")) + rule = new AnyChar; + else if (name == QLatin1String("DetectChar")) + rule = new DetectChar; + else if (name == QLatin1String("Detect2Chars")) + rule = new Detect2Char; + else if (name == QLatin1String("DetectIdentifier")) + rule = new DetectIdentifier; + else if (name == QLatin1String("DetectSpaces")) + rule = new DetectSpaces; + else if (name == QLatin1String("Float")) + rule = new Float; + else if (name == QLatin1String("Int")) + rule = new Int; + else if (name == QLatin1String("HlCChar")) + rule = new HlCChar; + else if (name == QLatin1String("HlCHex")) + rule = new HlCHex; + else if (name == QLatin1String("HlCOct")) + rule = new HlCOct; + else if (name == QLatin1String("HlCStringChar")) + rule = new HlCStringChar; + else if (name == QLatin1String("IncludeRules")) + rule = new IncludeRules; + else if (name == QLatin1String("keyword")) + rule = new KeywordListRule; + else if (name == QLatin1String("LineContinue")) + rule = new LineContinue; + else if (name == QLatin1String("RangeDetect")) + rule = new RangeDetect; + else if (name == QLatin1String("RegExpr")) + rule = new RegExpr; + else if (name == QLatin1String("StringDetect")) + rule = new StringDetect; + else if (name == QLatin1String("WordDetect")) + rule = new WordDetect; + else + qCWarning(Log) << "Unknown rule type:" << name; + + return Ptr(rule); +} + +bool Rule::isWordDelimiter(QChar c) const +{ + // perf tells contains is MUCH faster than binary search here, very short array + return m_wordDelimiter.contains(c); +} + + +bool AnyChar::doLoad(QXmlStreamReader& reader) +{ + m_chars = reader.attributes().value(QStringLiteral("String")).toString(); + if (m_chars.size() == 1) + qCDebug(Log) << "AnyChar rule with just one char: use DetectChar instead."; + return !m_chars.isEmpty(); +} + +MatchResult AnyChar::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (m_chars.contains(text.at(offset))) + return offset + 1; + return offset; +} + + +bool DetectChar::doLoad(QXmlStreamReader& reader) +{ + const auto s = reader.attributes().value(QStringLiteral("char")); + if (s.isEmpty()) + return false; + m_char = s.at(0); + m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic"))); + if (m_dynamic) { + m_captureIndex = m_char.digitValue(); + } + return true; +} + +MatchResult DetectChar::doMatch(const QString& text, int offset, const QStringList &captures) const +{ + if (m_dynamic) { + if (m_captureIndex == 0 || captures.size() <= m_captureIndex || captures.at(m_captureIndex).isEmpty()) + return offset; + if (text.at(offset) == captures.at(m_captureIndex).at(0)) + return offset + 1; + return offset; + } + + if (text.at(offset) == m_char) + return offset + 1; + return offset; +} + + +bool Detect2Char::doLoad(QXmlStreamReader& reader) +{ + const auto s1 = reader.attributes().value(QStringLiteral("char")); + const auto s2 = reader.attributes().value(QStringLiteral("char1")); + if (s1.isEmpty() || s2.isEmpty()) + return false; + m_char1 = s1.at(0); + m_char2 = s2.at(0); + return true; +} + +MatchResult Detect2Char::doMatch(const QString& text, int offset, const QStringList &) const +{ + if (text.size() - offset < 2) + return offset; + if (text.at(offset) == m_char1 && text.at(offset + 1) == m_char2) + return offset + 2; + return offset; +} + + +MatchResult DetectIdentifier::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (!text.at(offset).isLetter() && text.at(offset) != QLatin1Char('_')) + return offset; + + for (int i = offset + 1; i < text.size(); ++i) { + const auto c = text.at(i); + if (!c.isLetterOrNumber() && c != QLatin1Char('_')) + return i; + } + + return text.size(); +} + + +MatchResult DetectSpaces::doMatch(const QString& text, int offset, const QStringList&) const +{ + while(offset < text.size() && text.at(offset).isSpace()) + ++offset; + return offset; +} + + +MatchResult Float::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (offset > 0 && !isWordDelimiter(text.at(offset - 1))) + return offset; + + auto newOffset = offset; + while (newOffset < text.size() && text.at(newOffset).isDigit()) + ++newOffset; + + if (newOffset >= text.size() || text.at(newOffset) != QLatin1Char('.')) + return offset; + ++newOffset; + + while (newOffset < text.size() && text.at(newOffset).isDigit()) + ++newOffset; + + if (newOffset == offset + 1) // we only found a decimal point + return offset; + + auto expOffset = newOffset; + if (expOffset >= text.size() || (text.at(expOffset) != QLatin1Char('e') && text.at(expOffset) != QLatin1Char('E'))) + return newOffset; + ++expOffset; + + if (expOffset < text.size() && (text.at(expOffset) == QLatin1Char('+') || text.at(expOffset) == QLatin1Char('-'))) + ++expOffset; + bool foundExpDigit = false; + while (expOffset < text.size() && text.at(expOffset).isDigit()) { + ++expOffset; + foundExpDigit = true; + } + + if (!foundExpDigit) + return newOffset; + return expOffset; +} + + +MatchResult HlCChar::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (text.size() < offset + 3) + return offset; + + if (text.at(offset) != QLatin1Char('\'') || text.at(offset + 1) == QLatin1Char('\'')) + return offset; + + auto newOffset = matchEscapedChar(text, offset + 1); + if (newOffset == offset + 1) { + if (text.at(newOffset) == QLatin1Char('\\')) + return offset; + else + ++newOffset; + } + if (newOffset >= text.size()) + return offset; + + if (text.at(newOffset) == QLatin1Char('\'')) + return newOffset + 1; + + return offset; +} + + +MatchResult HlCHex::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (offset > 0 && !isWordDelimiter(text.at(offset - 1))) + return offset; + + if (text.size() < offset + 3) + return offset; + + if (text.at(offset) != QLatin1Char('0') || (text.at(offset + 1) != QLatin1Char('x') && text.at(offset + 1) != QLatin1Char('X'))) + return offset; + + if (!isHexChar(text.at(offset + 2))) + return offset; + + offset += 3; + while (offset < text.size() && isHexChar(text.at(offset))) + ++offset; + + // TODO Kate matches U/L suffix, QtC does not? + + return offset; +} + + +MatchResult HlCOct::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (offset > 0 && !isWordDelimiter(text.at(offset - 1))) + return offset; + + if (text.size() < offset + 2) + return offset; + + if (text.at(offset) != QLatin1Char('0')) + return offset; + + if (!isOctalChar(text.at(offset + 1))) + return offset; + + offset += 2; + while (offset < text.size() && isOctalChar(text.at(offset))) + ++offset; + + return offset; +} + + +MatchResult HlCStringChar::doMatch(const QString& text, int offset, const QStringList&) const +{ + return matchEscapedChar(text, offset); +} + + +QString IncludeRules::contextName() const +{ + return m_contextName; +} + +QString IncludeRules::definitionName() const +{ + return m_defName; +} + +bool IncludeRules::includeAttribute() const +{ + return m_includeAttribute; +} + +bool IncludeRules::doLoad(QXmlStreamReader& reader) +{ + const auto s = reader.attributes().value(QLatin1String("context")); + const auto split = s.split(QLatin1String("##"), QString::KeepEmptyParts); + if (split.isEmpty()) + return false; + m_contextName = split.at(0).toString(); + if (split.size() > 1) + m_defName = split.at(1).toString(); + m_includeAttribute = Xml::attrToBool(reader.attributes().value(QLatin1String("includeAttrib"))); + + return !m_contextName.isEmpty() || !m_defName.isEmpty(); +} + +MatchResult IncludeRules::doMatch(const QString& text, int offset, const QStringList&) const +{ + Q_UNUSED(text); + qCWarning(Log) << "Unresolved include rule for" << m_contextName << "##" << m_defName; + return offset; +} + + +MatchResult Int::doMatch(const QString& text, int offset, const QStringList &) const +{ + if (offset > 0 && !isWordDelimiter(text.at(offset - 1))) + return offset; + + while(offset < text.size() && text.at(offset).isDigit()) + ++offset; + return offset; +} + + +bool KeywordListRule::doLoad(QXmlStreamReader& reader) +{ + /** + * get our keyword list, if not found => bail out + */ + auto defData = DefinitionData::get(definition()); + m_keywordList = defData->keywordList(reader.attributes().value(QLatin1String("String")).toString()); + if (!m_keywordList) { + return false; + } + + /** + * we might overwrite the case sensitivity + * then we need to init the list for lookup of that sensitivity setting + */ + if (reader.attributes().hasAttribute(QLatin1String("insensitive"))) { + m_hasCaseSensitivityOverride = true; + m_caseSensitivityOverride = Xml::attrToBool(reader.attributes().value(QLatin1String("insensitive"))) ? + Qt::CaseInsensitive : Qt::CaseSensitive; + m_keywordList->initLookupForCaseSensitivity(m_caseSensitivityOverride); + } else { + m_hasCaseSensitivityOverride = false; + } + + return !m_keywordList->isEmpty(); +} + +MatchResult KeywordListRule::doMatch(const QString& text, int offset, const QStringList&) const +{ + auto newOffset = offset; + while (text.size() > newOffset && !isWordDelimiter(text.at(newOffset))) + ++newOffset; + if (newOffset == offset) + return offset; + + if (m_hasCaseSensitivityOverride) { + if (m_keywordList->contains(text.midRef(offset, newOffset - offset), m_caseSensitivityOverride)) + return newOffset; + } else { + if (m_keywordList->contains(text.midRef(offset, newOffset - offset))) + return newOffset; + } + + // we don't match, but we can skip until newOffset as we can't start a keyword in-between + return MatchResult(offset, newOffset); +} + + +bool LineContinue::doLoad(QXmlStreamReader& reader) +{ + const auto s = reader.attributes().value(QStringLiteral("char")); + if (s.isEmpty()) + m_char = QLatin1Char('\\'); + else + m_char = s.at(0); + return true; +} + +MatchResult LineContinue::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (offset == text.size() - 1 && text.at(offset) == m_char) + return offset + 1; + return offset; +} + + +bool RangeDetect::doLoad(QXmlStreamReader& reader) +{ + const auto s1 = reader.attributes().value(QStringLiteral("char")); + const auto s2 = reader.attributes().value(QStringLiteral("char1")); + if (s1.isEmpty() || s2.isEmpty()) + return false; + m_begin = s1.at(0); + m_end = s2.at(0); + return true; +} + +MatchResult RangeDetect::doMatch(const QString& text, int offset, const QStringList&) const +{ + if (text.size() - offset < 2) + return offset; + if (text.at(offset) != m_begin) + return offset; + + auto newOffset = offset + 1; + while (newOffset < text.size()) { + if (text.at(newOffset) == m_end) + return newOffset + 1; + ++newOffset; + } + return offset; +} + +bool RegExpr::doLoad(QXmlStreamReader& reader) +{ + m_regexp.setPattern(reader.attributes().value(QStringLiteral("String")).toString()); + + const auto isMinimal = Xml::attrToBool(reader.attributes().value(QStringLiteral("minimal"))); + const auto isCaseInsensitive = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive"))); + m_regexp.setPatternOptions( + (isMinimal ? QRegularExpression::InvertedGreedinessOption : QRegularExpression::NoPatternOption) | + (isCaseInsensitive ? QRegularExpression::CaseInsensitiveOption : QRegularExpression::NoPatternOption)); + + // optimize the pattern for the non-dynamic case, we use them OFTEN + m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic"))); + if (!m_dynamic) { + m_regexp.optimize(); + } + + // always using m_regexp.isValid() would be better, but parses the regexp and thus is way too expensive for release builds + + if (Log().isDebugEnabled()) { + if (!m_regexp.isValid()) + qCDebug(Log) << "Invalid regexp:" << m_regexp.pattern(); + } + return !m_regexp.pattern().isEmpty(); +} + +MatchResult RegExpr::doMatch(const QString& text, int offset, const QStringList &captures) const +{ + /** + * for dynamic case: create new pattern with right instantiation + */ + const auto ®exp = m_dynamic ? QRegularExpression(replaceCaptures(m_regexp.pattern(), captures, true), m_regexp.patternOptions()) : m_regexp; + + /** + * match the pattern + */ + const auto result = regexp.match(text, offset, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption); + if (result.capturedStart() == offset) { + /** + * we only need to compute the captured texts if we have real capture groups + * highlightings should only address %1..%.., see e.g. replaceCaptures + * DetectChar ignores %0, too + */ + if (result.lastCapturedIndex() > 0) { + return MatchResult(offset + result.capturedLength(), result.capturedTexts()); + } + + /** + * else: ignore the implicit 0 group we always capture, no need to allocate stuff for that + */ + return MatchResult(offset + result.capturedLength()); + } + + /** + * no match + */ + return MatchResult(offset, result.capturedStart()); +} + + +bool StringDetect::doLoad(QXmlStreamReader& reader) +{ + m_string = reader.attributes().value(QStringLiteral("String")).toString(); + m_caseSensitivity = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive"))) ? Qt::CaseInsensitive : Qt::CaseSensitive; + m_dynamic = Xml::attrToBool(reader.attributes().value(QStringLiteral("dynamic"))); + return !m_string.isEmpty(); +} + +MatchResult StringDetect::doMatch(const QString& text, int offset, const QStringList &captures) const +{ + /** + * for dynamic case: create new pattern with right instantiation + */ + const auto &pattern = m_dynamic ? replaceCaptures(m_string, captures, false) : m_string; + + if (text.midRef(offset, pattern.size()).compare(pattern, m_caseSensitivity) == 0) + return offset + pattern.size(); + return offset; +} + + +bool WordDetect::doLoad(QXmlStreamReader& reader) +{ + m_word = reader.attributes().value(QStringLiteral("String")).toString(); + m_caseSensitivity = Xml::attrToBool(reader.attributes().value(QStringLiteral("insensitive"))) ? Qt::CaseInsensitive : Qt::CaseSensitive; + return !m_word.isEmpty(); +} + +MatchResult WordDetect::doMatch(const QString& text, int offset, const QStringList &) const +{ + if (text.size() - offset < m_word.size()) + return offset; + + if (offset > 0 && !isWordDelimiter(text.at(offset - 1))) + return offset; + + if (text.midRef(offset, m_word.size()).compare(m_word, m_caseSensitivity) != 0) + return offset; + + if (text.size() == offset + m_word.size() || isWordDelimiter(text.at(offset + m_word.size()))) + return offset + m_word.size(); + + return offset; +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h new file mode 100644 index 0000000000..538fdeda8a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h @@ -0,0 +1,291 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_RULE_P_H +#define KSYNTAXHIGHLIGHTING_RULE_P_H + +#include "contextswitch_p.h" +#include "definition.h" +#include "definitionref_p.h" +#include "foldingregion.h" +#include "format.h" +#include "keywordlist_p.h" +#include "matchresult_p.h" + +#include <QRegularExpression> +#include <QString> +#include <QVector> + +#include <memory> + +QT_BEGIN_NAMESPACE +class QXmlStreamReader; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +class Rule +{ +public: + Rule() = default; + virtual ~Rule() = default; + + typedef std::shared_ptr<Rule> Ptr; + + Definition definition() const; + void setDefinition(const Definition &def); + + const Format &attributeFormat() const + { + return m_attributeFormat; + } + + const ContextSwitch &context() const + { + return m_context; + } + + bool isLookAhead() const + { + return m_lookAhead; + } + + bool firstNonSpace() const + { + return m_firstNonSpace; + } + + int requiredColumn() const + { + return m_column; + } + + const FoldingRegion &beginRegion() const + { + return m_beginRegion; + } + + const FoldingRegion &endRegion() const + { + return m_endRegion; + } + + bool load(QXmlStreamReader &reader); + void resolveContext(); + void resolveAttributeFormat(Context *lookupContext); + + virtual MatchResult doMatch(const QString &text, int offset, const QStringList &captures) const = 0; + + static Rule::Ptr create(const QStringRef &name); + +protected: + virtual bool doLoad(QXmlStreamReader &reader); + + bool isWordDelimiter(QChar c) const; + +private: + Q_DISABLE_COPY(Rule) + + DefinitionRef m_def; + QString m_attribute; + Format m_attributeFormat; + ContextSwitch m_context; + int m_column = -1; + FoldingRegion m_beginRegion; + FoldingRegion m_endRegion; + bool m_firstNonSpace = false; + bool m_lookAhead = false; + + // cache for DefinitionData::wordDelimiters, is accessed VERY often + QStringRef m_wordDelimiter; +}; + + +class AnyChar : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; + +private: + QString m_chars; +}; + +class DetectChar : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; + +private: + QChar m_char; + bool m_dynamic = false; + int m_captureIndex = 0; +}; + +class Detect2Char : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; + +private: + QChar m_char1; + QChar m_char2; +}; + +class DetectIdentifier : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class DetectSpaces : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class Float : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class IncludeRules : public Rule +{ +public: + QString contextName() const; + QString definitionName() const; + bool includeAttribute() const; + +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; + +private: + QString m_contextName; + QString m_defName; + bool m_includeAttribute; +}; + +class Int : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; +}; + +class HlCChar : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class HlCHex : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class HlCOct : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class HlCStringChar : public Rule +{ +protected: + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; +}; + +class KeywordListRule : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; + +private: + KeywordList *m_keywordList; + bool m_hasCaseSensitivityOverride; + Qt::CaseSensitivity m_caseSensitivityOverride; +}; + +class LineContinue : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; + +private: + QChar m_char; +}; + +class RangeDetect : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList&) const override; + +private: + QChar m_begin; + QChar m_end; +}; + +class RegExpr : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; + +private: + QRegularExpression m_regexp; + bool m_dynamic = false; +}; + +class StringDetect : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; + +private: + QString m_string; + Qt::CaseSensitivity m_caseSensitivity; + bool m_dynamic = false; +}; + +class WordDetect : public Rule +{ +protected: + bool doLoad(QXmlStreamReader & reader) override; + MatchResult doMatch(const QString & text, int offset, const QStringList &captures) const override; + +private: + QString m_word; + Qt::CaseSensitivity m_caseSensitivity; +}; + +} + +#endif // KSYNTAXHIGHLIGHTING_RULE_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp new file mode 100644 index 0000000000..f970e13f8b --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp @@ -0,0 +1,123 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "state.h" +#include "state_p.h" + +#include "context_p.h" + +#include <QStringList> + +using namespace KSyntaxHighlighting; + +StateData* StateData::get(State &state) +{ + state.d.detach(); + return state.d.data(); +} + +bool StateData::isEmpty() const +{ + return m_contextStack.isEmpty(); +} + +void StateData::clear() +{ + m_contextStack.clear(); +} + +int StateData::size() const +{ + return m_contextStack.size(); +} + +void StateData::push(Context *context, const QStringList &captures) +{ + Q_ASSERT(context); + m_contextStack.push_back(qMakePair(context, captures)); +} + +bool StateData::pop(int popCount) +{ + // nop if nothing to pop + if (popCount <= 0) { + return true; + } + + // keep the initial context alive in any case + Q_ASSERT(!isEmpty()); + const bool initialContextSurvived = m_contextStack.size() > popCount; + m_contextStack.resize(std::max(1, m_contextStack.size() - popCount)); + return initialContextSurvived; +} + +Context* StateData::topContext() const +{ + Q_ASSERT(!isEmpty()); + return m_contextStack.last().first; +} + +const QStringList &StateData::topCaptures() const +{ + Q_ASSERT(!isEmpty()); + return m_contextStack.last().second; +} + +State::State() : + d(new StateData) +{ +} + +State::State(const State &other) : + d(other.d) +{ +} + +State::~State() +{ +} + +State& State::operator=(const State &other) +{ + d = other.d; + return *this; +} + +bool State::operator==(const State &other) const +{ + // use pointer equal as shortcut for shared states + return (d == other.d) || (d->m_contextStack == other.d->m_contextStack && d->m_defRef == other.d->m_defRef); +} + +bool State::operator!=(const State &other) const +{ + return !(*this == other); +} + +bool State::indentationBasedFoldingEnabled() const +{ + if (d->m_contextStack.isEmpty()) + return false; + return d->m_contextStack.last().first->indentationBasedFoldingEnabled(); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h new file mode 100644 index 0000000000..fce4bc71e8 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h @@ -0,0 +1,86 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_STATE_H +#define KSYNTAXHIGHLIGHTING_STATE_H + +#include "ksyntaxhighlighting_export.h" + +#include <QExplicitlySharedDataPointer> +#include <QTypeInfo> + +namespace KSyntaxHighlighting { + +class StateData; + +/** Opaque handle to the state of the highlighting engine. + * This needs to be fed into AbstractHighlighter for every line of text + * and allows concrete highlighter implementations to store state per + * line for fast re-highlighting of specific lines (e.g. during editing). + * + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT State +{ +public: + /** Creates an initial state, ie. what should be used for the first line + * in a document. + */ + State(); + State(const State &other); + ~State(); + State& operator=(const State &rhs); + + /** Compares two states for equality. + * For two equal states and identical text input, AbstractHighlighter + * guarantees to produce equal results. This can be used to only + * re-highlight as many lines as necessary during editing. + */ + bool operator==(const State &other) const; + /** Compares two states for inequality. + * This is the opposite of operator==(). + */ + bool operator!=(const State &other) const; + + /** + * Returns whether or not indentation-based folding is enabled in this state. + * When using a Definition with indentation-based folding, use + * this method to check if indentation-based folding has been + * suspended in the current line. + * + * @see Definition::indentationBasedFoldingEnabled() + */ + bool indentationBasedFoldingEnabled() const; + +private: + friend class StateData; + QExplicitlySharedDataPointer<StateData> d; +}; + +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // KSYNTAXHIGHLIGHTING_STATE_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h new file mode 100644 index 0000000000..a99192b4c6 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h @@ -0,0 +1,81 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2018 Christoph Cullmann <cullmann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_STATE_P_H +#define KSYNTAXHIGHLIGHTING_STATE_P_H + +#include <QSharedData> +#include <QVector> + +#include "definitionref_p.h" + +QT_BEGIN_NAMESPACE +class QStringList; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting +{ + +class Context; + +class StateData : public QSharedData +{ + friend class State; + friend class AbstractHighlighter; + +public: + StateData() = default; + static StateData* get(State &state); + + bool isEmpty() const; + void clear(); + int size() const; + void push(Context *context, const QStringList &captures); + + /** + * Pop the number of elements given from the top of the current stack. + * Will not pop the initial element. + * @param popCount number of elements to pop + * @return false if one has tried to pop the initial context, else true + */ + bool pop(int popCount); + + Context* topContext() const; + const QStringList &topCaptures() const; + +private: + /** + * weak reference to the used definition to filter out invalid states + */ + DefinitionRef m_defRef; + + /** + * the context stack combines the active context + valid captures + */ + QVector<QPair<Context *, QStringList>> m_contextStack; +}; + +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp new file mode 100644 index 0000000000..2bb61a7ae6 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp @@ -0,0 +1,198 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "syntaxhighlighter.h" +#include "abstracthighlighter_p.h" +#include "definition.h" +#include "foldingregion.h" +#include "format.h" +#include "state.h" +#include "theme.h" + +#include <QDebug> + +Q_DECLARE_METATYPE(QTextBlock) + +using namespace KSyntaxHighlighting; + +namespace KSyntaxHighlighting { +class TextBlockUserData : public QTextBlockUserData +{ +public: + State state; + QVector<FoldingRegion> foldingRegions; +}; + +class SyntaxHighlighterPrivate : public AbstractHighlighterPrivate +{ +public: + static FoldingRegion foldingRegion(const QTextBlock &startBlock); + QVector<FoldingRegion> foldingRegions; +}; + +} + +FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock& startBlock) +{ + const auto data = dynamic_cast<TextBlockUserData*>(startBlock.userData()); + if (!data) + return FoldingRegion(); + for (int i = data->foldingRegions.size() - 1; i >= 0; --i) { + if (data->foldingRegions.at(i).type() == FoldingRegion::Begin) + return data->foldingRegions.at(i); + } + return FoldingRegion(); +} + +SyntaxHighlighter::SyntaxHighlighter(QObject* parent) : + QSyntaxHighlighter(parent), + AbstractHighlighter(new SyntaxHighlighterPrivate) +{ + qRegisterMetaType<QTextBlock>(); +} + +SyntaxHighlighter::SyntaxHighlighter(QTextDocument *document) : + QSyntaxHighlighter(document), + AbstractHighlighter(new SyntaxHighlighterPrivate) +{ + qRegisterMetaType<QTextBlock>(); +} + +SyntaxHighlighter::~SyntaxHighlighter() +{ +} + +void SyntaxHighlighter::setDefinition(const Definition& def) +{ + const auto needsRehighlight = definition() != def; + AbstractHighlighter::setDefinition(def); + if (needsRehighlight) + rehighlight(); +} + +bool SyntaxHighlighter::startsFoldingRegion(const QTextBlock &startBlock) const +{ + return SyntaxHighlighterPrivate::foldingRegion(startBlock).type() == FoldingRegion::Begin; +} + +QTextBlock SyntaxHighlighter::findFoldingRegionEnd(const QTextBlock &startBlock) const +{ + const auto region = SyntaxHighlighterPrivate::foldingRegion(startBlock); + + auto block = startBlock; + int depth = 1; + while (block.isValid()) { + block = block.next(); + const auto data = dynamic_cast<TextBlockUserData*>(block.userData()); + if (!data) + continue; + for (auto it = data->foldingRegions.constBegin(); it != data->foldingRegions.constEnd(); ++it) { + if ((*it).id() != region.id()) + continue; + if ((*it).type() == FoldingRegion::End) + --depth; + else if ((*it).type() == FoldingRegion::Begin) + ++depth; + if (depth == 0) + return block; + } + } + + return QTextBlock(); +} + +void SyntaxHighlighter::highlightBlock(const QString& text) +{ + Q_D(SyntaxHighlighter); + + State state; + if (currentBlock().position() > 0) { + const auto prevBlock = currentBlock().previous(); + const auto prevData = dynamic_cast<TextBlockUserData*>(prevBlock.userData()); + if (prevData) + state = prevData->state; + } + d->foldingRegions.clear(); + state = highlightLine(text, state); + + auto data = dynamic_cast<TextBlockUserData*>(currentBlockUserData()); + if (!data) { // first time we highlight this + data = new TextBlockUserData; + data->state = state; + data->foldingRegions = d->foldingRegions; + setCurrentBlockUserData(data); + return; + } + + if (data->state == state && data->foldingRegions == d->foldingRegions) // we ended up in the same state, so we are done here + return; + data->state = state; + data->foldingRegions = d->foldingRegions; + + const auto nextBlock = currentBlock().next(); + if (nextBlock.isValid()) + QMetaObject::invokeMethod(this, "rehighlightBlock", Qt::QueuedConnection, Q_ARG(QTextBlock, nextBlock)); +} + +void SyntaxHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format& format) +{ + if (format.isDefaultTextStyle(theme()) || length == 0) + return; + + QTextCharFormat tf; + if (format.hasTextColor(theme())) + tf.setForeground(format.textColor(theme())); + if (format.hasBackgroundColor(theme())) + tf.setBackground(format.backgroundColor(theme())); + + if (format.isBold(theme())) + tf.setFontWeight(QFont::Bold); + if (format.isItalic(theme())) + tf.setFontItalic(true); + if (format.isUnderline(theme())) + tf.setFontUnderline(true); + if (format.isStrikeThrough(theme())) + tf.setFontStrikeOut(true); + + QSyntaxHighlighter::setFormat(offset, length, tf); +} + +void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion region) +{ + Q_UNUSED(offset); + Q_UNUSED(length); + Q_D(SyntaxHighlighter); + + if (region.type() == FoldingRegion::Begin) + d->foldingRegions.push_back(region); + + if (region.type() == FoldingRegion::End) { + for (int i = d->foldingRegions.size() - 1; i >= 0; --i) { + if (d->foldingRegions.at(i).id() != region.id() || d->foldingRegions.at(i).type() != FoldingRegion::Begin) + continue; + d->foldingRegions.remove(i); + return; + } + d->foldingRegions.push_back(region); + } +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h new file mode 100644 index 0000000000..f5d2a5e219 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h @@ -0,0 +1,85 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_QSYNTAXHIGHLIGHTER_H +#define KSYNTAXHIGHLIGHTING_QSYNTAXHIGHLIGHTER_H + +#include "ksyntaxhighlighting_export.h" + +#include "abstracthighlighter.h" + +#include <QSyntaxHighlighter> + +namespace KSyntaxHighlighting { + +class SyntaxHighlighterPrivate; + +/** A QSyntaxHighlighter implementation for use with QTextDocument. + * This supports partial re-highlighting during editing and + * tracks syntax-based code folding regions. + * + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT SyntaxHighlighter : public QSyntaxHighlighter, public AbstractHighlighter +{ + Q_OBJECT +public: + explicit SyntaxHighlighter(QObject *parent = nullptr); + explicit SyntaxHighlighter(QTextDocument *document); + ~SyntaxHighlighter() override; + + void setDefinition(const Definition &def) override; + + /** Returns whether there is a folding region beginning at @p startBlock. + * This only considers syntax-based folding regions, + * not indention-based ones as e.g. found in Python. + * + * @see findFoldingRegionEnd + */ + bool startsFoldingRegion(const QTextBlock &startBlock) const; + + /** Finds the end of the folding region starting at @p startBlock. + * If multiple folding regions begin at @p startBlock, the end of + * the last/innermost one is returned. + * This returns an invalid block if no folding region end is found, + * which typically indicates an unterminated region and thus folding + * until the document end. + * This method performs a sequential search starting at @p startBlock + * for the matching folding region end, which is a potentially expensive + * operation. + * + * @see startsFoldingRegion + */ + QTextBlock findFoldingRegionEnd(const QTextBlock &startBlock) const; + +protected: + void highlightBlock(const QString & text) override; + void applyFormat(int offset, int length, const Format &format) override; + void applyFolding(int offset, int length, FoldingRegion region) override; + +private: + Q_DECLARE_PRIVATE_D(AbstractHighlighter::d_ptr, SyntaxHighlighter) +}; +} + +#endif // KSYNTAXHIGHLIGHTING_QSYNTAXHIGHLIGHTER_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h new file mode 100644 index 0000000000..40c5ef679e --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/textstyledata_p.h @@ -0,0 +1,63 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_TEXTSTYLEDATA_P_H +#define KSYNTAXHIGHLIGHTING_TEXTSTYLEDATA_P_H + +#include <QColor> + +namespace KSyntaxHighlighting { + +class TextStyleData +{ +public: + // Constructor initializing all data. + TextStyleData() + : bold(false) + , italic(false) + , underline(false) + , strikeThrough(false) + , hasBold(false) + , hasItalic(false) + , hasUnderline(false) + , hasStrikeThrough(false) + {} + + QRgb textColor = 0x0; + QRgb backgroundColor = 0x0; + QRgb selectedTextColor = 0x0; + QRgb selectedBackgroundColor = 0x0; + bool bold :1; + bool italic :1; + bool underline :1; + bool strikeThrough :1; + + bool hasBold :1; + bool hasItalic :1; + bool hasUnderline :1; + bool hasStrikeThrough :1; +}; + +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp new file mode 100644 index 0000000000..57f62ef6ab --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp @@ -0,0 +1,124 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "theme.h" +#include "themedata_p.h" + +#include <QCoreApplication> + +using namespace KSyntaxHighlighting; + +Theme::Theme() +{ +} + +Theme::Theme(const Theme ©) +{ + m_data = copy.m_data; +} + +Theme::Theme(ThemeData* 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.data(); +} + +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; +} + +QRgb Theme::editorColor(EditorColorRole role) const +{ + return m_data ? m_data->editorColor(role) : 0; +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h new file mode 100644 index 0000000000..adb8431f6a --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h @@ -0,0 +1,378 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_THEME_H +#define KSYNTAXHIGHLIGHTING_THEME_H + +#include "ksyntaxhighlighting_export.h" + +#include <QColor> +#include <QExplicitlySharedDataPointer> +#include <qobjectdefs.h> +#include <QTypeInfo> + +namespace KSyntaxHighlighting { + +class ThemeData; +class RepositoryPrivate; + +/** + * 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" and accessible by calling + * editorColor() with the desired enum EditorColorRole. + * + * @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 + * @since 5.28 + */ +class KSYNTAXHIGHLIGHTING_EXPORT Theme +{ + Q_GADGET +public: + + // TODO KF6: + // - make TextStyle an enum class + // - move out of Theme into KSyntaxHighlighting + // - do the same for EditorColorRole + + /** + * Default styles that can be referenced from syntax definition XML files. + * Make sure to choose readable colors with good contrast especially in + * combination with the EditorColorRole%s. + */ + enum TextStyle { + //! Default text style for normal text and source code without + //! special highlighting. + Normal = 0, + //! Text style for language keywords. + Keyword, + //! Text style for function definitions and function calls. + Function, + //! Text style for variables, if applicable. For instance, variables in + //! PHP typically start with a '$', so all identifiers following the + //! pattern $foo are highlighted as variable. + Variable, + //! Text style for control flow highlighting, such as @e if, @e then, + //! @e else, @e return, or @e continue. + ControlFlow, + //! Text style for operators such as +, -, *, / and :: etc. + Operator, + //! Text style for built-in language classes and functions. + BuiltIn, + //! Text style for well-known extensions, such as Qt or boost. + Extension, + //! Text style for preprocessor statements. + Preprocessor, + //! Text style for attributes of functions or objects, e.g. \@override + //! in Java, or __declspec(...) and __attribute__((...)) in C++. + Attribute, + //! Text style for single characters such as 'a'. + Char, + //! Text style for escaped characters in strings, such as "hello\n". + SpecialChar, + //! Text style for strings, for instance "hello world". + String, + //! Text style for verbatim strings such as HERE docs. + VerbatimString, + //! Text style for special strings such as regular expressions in + //! ECMAScript or the LaTeX math mode. + SpecialString, + //! Text style for includes, imports, modules, or LaTeX packages. + Import, + //! Text style for data types such as int, char, float etc. + DataType, + //! Text style for decimal values. + DecVal, + //! Text style for numbers with base other than 10. + BaseN, + //! Text style for floating point numbers. + Float, + //! Text style for language constants, e.g. True, False, None in Python + //! or nullptr in C/C++. + Constant, + //! Text style for normal comments. + Comment, + //! Text style for comments that reflect API documentation, such as + //! doxygen /** */ comments. + Documentation, + //! Text style for annotations in comments, such as \@param in Doxygen + //! or JavaDoc. + Annotation, + //! Text style that refers to variables in a comment, such as after + //! \@param \<identifier\> in Doxygen or JavaDoc. + CommentVar, + //! Text style for region markers, typically defined by BEGIN/END. + RegionMarker, + //! Text style for information, such as the keyword \@note in Doxygen. + Information, + //! Text style for warnings, such as the keyword \@warning in Doxygen. + Warning, + //! Text style for comment specials such as TODO and WARNING in + //! comments. + Alert, + //! Text style indicating wrong syntax. + Error, + //! Text style for attributes that do not match any of the other default + //! styles. + Others + }; + Q_ENUM(TextStyle) + + /** + * Editor color roles, used to paint line numbers, editor background etc. + * The colors typically should have good contrast with the colors used + * in the TextStyle%s. + */ + enum EditorColorRole { + //! Background color for the editing area. + BackgroundColor = 0, + //! Background color for selected text. + TextSelection, + //! Background color for the line of the current text cursor. + CurrentLine, + //! Background color for matching text while searching. + SearchHighlight, + //! Background color for replaced text for a search & replace action. + ReplaceHighlight, + //! Background color for matching bracket pairs (including quotes) + BracketMatching, + //! Foreground color for visualizing tabs and trailing spaces. + TabMarker, + //! Color used to underline spell check errors. + SpellChecking, + //! Color used to draw vertical indentation levels, typically a line. + IndentationLine, + //! Background color for the icon border. + IconBorder, + //! Background colors for code folding regions in the text area, as well + //! as code folding indicators in the code folding border. + CodeFolding, + //! Foreground color for drawing the line numbers. This should have a + //! good contrast with the IconBorder background color. + LineNumbers, + //! Foreground color for drawing the current line number. This should + //! have a good contrast with the IconBorder background color. + CurrentLineNumber, + //! Color used in the icon border to indicate dynamically wrapped lines. + //! This color should have a good contrast with the IconBorder + //! background color. + WordWrapMarker, + //! Color used to draw a vertical line for marking changed lines. + ModifiedLines, + //! Color used to draw a vertical line for marking saved lines. + SavedLines, + //! Line color used to draw separator lines, e.g. at column 80 in the + //! text editor area. + Separator, + //! Background color for bookmarks. + MarkBookmark, + //! Background color for active breakpoints. + MarkBreakpointActive, + //! Background color for a reached breakpoint. + MarkBreakpointReached, + //! Background color for inactive (disabled) breakpoints. + MarkBreakpointDisabled, + //! Background color for marking the current execution position. + MarkExecution, + //! Background color for general warning marks. + MarkWarning, + //! Background color for general error marks. + MarkError, + //! Background color for text templates (snippets). + TemplateBackground, + //! Background color for all editable placeholders in text templates. + TemplatePlaceholder, + //! Background color for the currently active placeholder in text + //! templates. + TemplateFocusedPlaceholder, + //! Background color for read-only placeholders in text templates. + TemplateReadOnlyPlaceholder + }; + Q_ENUM(EditorColorRole) + + /** + * 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. + * + * A Theme is read-only, if the filePath() points to a non-writable file. + * This is typically the case for Themes that are compiled into the executable + * as resource file, as well as for theme files that are installed in read-only + * system locations (e.g. /usr/share/). + */ + bool isReadOnly() const; + + /** + * Returns the full path and file name 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 struck through. + */ + bool isStrikeThrough(TextStyle style) const; + +public: + /** + * Returns the editor color for the requested @p role. + */ + QRgb editorColor(EditorColorRole role) const; + +private: + /** + * Constructor taking a shared ThemeData instance. + */ + explicit Theme(ThemeData* data); + friend class RepositoryPrivate; + friend class ThemeData; + +private: + /** + * Shared data holder. + */ + QExplicitlySharedDataPointer<ThemeData> m_data; +}; + +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // KSYNTAXHIGHLIGHTING_THEME_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp new file mode 100644 index 0000000000..eac9a92264 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp @@ -0,0 +1,255 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2016 Dominik Haumann <dhaumann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "themedata_p.h" +#include "ksyntaxhighlighting_logging.h" + +#include <QFile> +#include <QFileInfo> +#include <QJsonDocument> +#include <QJsonObject> +#include <QJsonValue> +#include <QMetaEnum> + +#include <QDebug> + +using namespace KSyntaxHighlighting; + +ThemeData* ThemeData::get(const Theme &theme) +{ + return theme.m_data.data(); +} + +ThemeData::ThemeData() +{ + memset(m_editorColors, 0, sizeof(m_editorColors)); +} + +/** + * 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; +} + +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"))); + + auto val = obj.value(QLatin1String("bold")); + if (val.isBool()) { + td.bold = val.toBool(); + td.hasBold = true; + } + val = obj.value(QLatin1String("italic")); + if (val.isBool()) { + td.italic = val.toBool(); + td.hasItalic = true; + } + val = obj.value(QLatin1String("underline")); + if (val.isBool()) { + td.underline = val.toBool(); + td.hasUnderline = true; + } + val = obj.value(QLatin1String("strike-through")); + if (val.isBool()) { + td.strikeThrough = val.toBool(); + td.hasStrikeThrough = true; + } + + 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) { + qCWarning(Log) << "Failed to parse theme file" << filePath << ":" << parseError.errorString(); + 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(); + + // 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()); + } + + // read editor area colors + const QJsonObject editorColors = obj.value(QLatin1String("editor-colors")).toObject(); + m_editorColors[Theme::BackgroundColor] = readColor(editorColors.value(QLatin1String("background-color"))); + m_editorColors[Theme::TextSelection] = readColor(editorColors.value(QLatin1String("selection"))); + m_editorColors[Theme::CurrentLine] = readColor(editorColors.value(QLatin1String("current-line"))); + m_editorColors[Theme::SearchHighlight] = readColor(editorColors.value(QLatin1String("search-highlight"))); + m_editorColors[Theme::ReplaceHighlight] = readColor(editorColors.value(QLatin1String("replace-highlight"))); + m_editorColors[Theme::BracketMatching] = readColor(editorColors.value(QLatin1String("bracket-matching"))); + m_editorColors[Theme::TabMarker] = readColor(editorColors.value(QLatin1String("tab-marker"))); + m_editorColors[Theme::SpellChecking] = readColor(editorColors.value(QLatin1String("spell-checking"))); + m_editorColors[Theme::IndentationLine] = readColor(editorColors.value(QLatin1String("indentation-line"))); + m_editorColors[Theme::IconBorder] = readColor(editorColors.value(QLatin1String("icon-border"))); + m_editorColors[Theme::CodeFolding] = readColor(editorColors.value(QLatin1String("code-folding"))); + m_editorColors[Theme::LineNumbers] = readColor(editorColors.value(QLatin1String("line-numbers"))); + m_editorColors[Theme::CurrentLineNumber] = readColor(editorColors.value(QLatin1String("current-line-number"))); + m_editorColors[Theme::WordWrapMarker] = readColor(editorColors.value(QLatin1String("word-wrap-marker"))); + m_editorColors[Theme::ModifiedLines] = readColor(editorColors.value(QLatin1String("modified-lines"))); + m_editorColors[Theme::SavedLines] = readColor(editorColors.value(QLatin1String("saved-lines"))); + m_editorColors[Theme::Separator] = readColor(editorColors.value(QLatin1String("separator"))); + m_editorColors[Theme::MarkBookmark] = readColor(editorColors.value(QLatin1String("mark-bookmark"))); + m_editorColors[Theme::MarkBreakpointActive] = readColor(editorColors.value(QLatin1String("mark-breakpoint-active"))); + m_editorColors[Theme::MarkBreakpointReached] = readColor(editorColors.value(QLatin1String("mark-breakpoint-reached"))); + m_editorColors[Theme::MarkBreakpointDisabled] = readColor(editorColors.value(QLatin1String("mark-breakpoint-disabled"))); + m_editorColors[Theme::MarkExecution] = readColor(editorColors.value(QLatin1String("mark-execution"))); + m_editorColors[Theme::MarkWarning] = readColor(editorColors.value(QLatin1String("mark-warning"))); + m_editorColors[Theme::MarkError] = readColor(editorColors.value(QLatin1String("mark-error"))); + m_editorColors[Theme::TemplateBackground] = readColor(editorColors.value(QLatin1String("template-background"))); + m_editorColors[Theme::TemplatePlaceholder] = readColor(editorColors.value(QLatin1String("template-placeholder"))); + m_editorColors[Theme::TemplateFocusedPlaceholder] = readColor(editorColors.value(QLatin1String("template-focused-placeholder"))); + m_editorColors[Theme::TemplateReadOnlyPlaceholder] = readColor(editorColors.value(QLatin1String("template-read-only-placeholder"))); + + // read per-definition style overrides + const auto customStyles = obj.value(QLatin1String("custom-styles")).toObject(); + for (auto it = customStyles.begin(); it != customStyles.end(); ++it) { + const auto obj = it.value().toObject(); + QHash<QString, TextStyleData> overrideStyle; + for (auto it2 = obj.begin(); it2 != obj.end(); ++it2) + overrideStyle.insert(it2.key(), readThemeData(it2.value().toObject())); + m_textStyleOverrides.insert(it.key(), overrideStyle); + } + + return true; +} + +QString ThemeData::name() const +{ + return m_name; +} + +int ThemeData::revision() const +{ + return m_revision; +} + +bool ThemeData::isReadOnly() const +{ + return !QFileInfo(m_filePath).isWritable(); +} + +QString ThemeData::filePath() const +{ + return m_filePath; +} + +QRgb ThemeData::textColor(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].textColor; +} + +QRgb ThemeData::selectedTextColor(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].selectedTextColor; +} + +QRgb ThemeData::backgroundColor(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].backgroundColor; +} + +QRgb ThemeData::selectedBackgroundColor(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].selectedBackgroundColor; +} + +bool ThemeData::isBold(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].bold; +} + +bool ThemeData::isItalic(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].italic; +} + +bool ThemeData::isUnderline(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].underline; +} + +bool ThemeData::isStrikeThrough(Theme::TextStyle style) const +{ + Q_ASSERT(static_cast<int>(style) >= 0 && static_cast<int>(style) <= static_cast<int>(Theme::Others)); + return m_textStyles[style].strikeThrough; +} + +QRgb ThemeData::editorColor(Theme::EditorColorRole role) const +{ + Q_ASSERT(static_cast<int>(role) >= 0 && static_cast<int>(role) <= static_cast<int>(Theme::TemplateReadOnlyPlaceholder)); + return m_editorColors[role]; +} + +TextStyleData ThemeData::textStyleOverride(const QString& definitionName, const QString& attributeName) const +{ + return m_textStyleOverrides.value(definitionName).value(attributeName); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h new file mode 100644 index 0000000000..3b5f4637a9 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h @@ -0,0 +1,170 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + Copyright (C) 2016 Dominik Haumann <dhaumann@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_THEMEDATA_P_H +#define KSYNTAXHIGHLIGHTING_THEMEDATA_P_H + +#include "theme.h" +#include "textstyledata_p.h" + +#include <QHash> +#include <QSharedData> + +namespace KSyntaxHighlighting { + +/** + * Data container for a Theme. + */ +class ThemeData : public QSharedData +{ +public: + static ThemeData* get(const Theme &theme); + + /** + * 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 the revision of this Theme. + * The revision in a .theme file should be increased with every change. + */ + int revision() 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 struck through. + */ + bool isStrikeThrough(Theme::TextStyle style) const; + +public: + /** + * Returns the editor color for the requested @p role. + */ + QRgb editorColor(Theme::EditorColorRole role) const; + + /** + * Returns the TextStyle override of a specific "itemData" with attributeName + * in the syntax definition called definitionName. + * + * If no override exists, a valid TextStyleData with the respective default + * TextStyle will be used, so the returned value is always valid. + */ + TextStyleData textStyleOverride(const QString &definitionName, const QString &attributeName) const; + +private: + int m_revision = 0; + QString m_name; + + //! Path to the file where the theme came from. + //! This is either a resource location (":/themes/Default.theme"), or a file + //! on disk (in a read-only or a writeable location). + QString m_filePath; + + //! TextStyles + TextStyleData m_textStyles[Theme::Others + 1]; + + //! style overrides for individual itemData entries + //! definition name -> attribute name -> style + QHash<QString, QHash<QString, TextStyleData> > m_textStyleOverrides; + + //! Editor area colors + QRgb m_editorColors[Theme::TemplateReadOnlyPlaceholder + 1]; +}; + +} + +QT_BEGIN_NAMESPACE +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_MOVABLE_TYPE); +QT_END_NAMESPACE + +#endif // KSYNTAXHIGHLIGHTING_THEMEDATA_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp new file mode 100644 index 0000000000..167295a930 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher.cpp @@ -0,0 +1,83 @@ +/* + Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include "wildcardmatcher_p.h" + +using namespace KSyntaxHighlighting; + +#include <QString> +#include <QChar> + +static bool exactMatch(const QString &candidate, const QString &wildcard, int candidatePosFromRight, + int wildcardPosFromRight, bool caseSensitive = true) +{ + for (; wildcardPosFromRight >= 0; wildcardPosFromRight--) { + const auto ch = wildcard.at(wildcardPosFromRight).unicode(); + switch (ch) { + case L'*': + if (candidatePosFromRight == -1) { + break; + } + + if (wildcardPosFromRight == 0) { + return true; + } + + // Eat all we can and go back as far as we have to + for (int j = -1; j <= candidatePosFromRight; j++) { + if (exactMatch(candidate, wildcard, j, wildcardPosFromRight - 1)) { + return true; + } + } + return false; + + case L'?': + if (candidatePosFromRight == -1) { + return false; + } + + candidatePosFromRight--; + break; + + default: + if (candidatePosFromRight == -1) { + return false; + } + + const auto candidateCh = candidate.at(candidatePosFromRight).unicode(); + const auto match = caseSensitive ? (candidateCh == ch) : (QChar::toLower(candidateCh) == QChar::toLower(ch)); + if (match) { + candidatePosFromRight--; + } else { + return false; + } + } + } + return true; +} + +bool WildcardMatcher::exactMatch(const QString &candidate, const QString &wildcard, + bool caseSensitive) +{ + return ::exactMatch(candidate, wildcard, candidate.length() - 1, wildcard.length() - 1, caseSensitive); +} diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h new file mode 100644 index 0000000000..016b10fe66 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/wildcardmatcher_p.h @@ -0,0 +1,51 @@ +/* + Copyright (C) 2007 Sebastian Pipping <webmaster@hartwork.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H +#define KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H + +#include <QtGlobal> + +QT_BEGIN_NAMESPACE +class QString; +QT_END_NAMESPACE + +namespace KSyntaxHighlighting { + +namespace WildcardMatcher +{ + /** + * Matches a string against a given wildcard. + * The wildcard supports '*' (".*" in regex) and '?' ("." in regex), not more. + * + * @param candidate Text to match + * @param wildcard Wildcard to use + * @param caseSensitive Case-sensitivity flag + * @return True for an exact match, false otherwise + */ + bool exactMatch(const QString &candidate, const QString &wildcard, bool caseSensitive = true); +} + +} + +#endif // KSYNTAXHIGHLIGHTING_WILDCARDMATCHER_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h new file mode 100644 index 0000000000..5f1f066dfd --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/xml_p.h @@ -0,0 +1,42 @@ +/* + Copyright (C) 2016 Volker Krause <vkrause@kde.org> + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef KSYNTAXHIGHLIGHTING_XML_P_H +#define KSYNTAXHIGHLIGHTING_XML_P_H + +#include <QString> + +namespace KSyntaxHighlighting { +/** Utilities for XML parsing. */ +namespace Xml { + +/** Parse a xs:boolean attribute. */ +inline bool attrToBool(const QStringRef &str) +{ + return str == QLatin1String("1") || str.compare(QLatin1String("true"), Qt::CaseInsensitive) == 0; +} + +} +} + +#endif |