diff options
author | David Schulz <david.schulz@qt.io> | 2024-02-13 05:18:33 +0100 |
---|---|---|
committer | David Schulz <david.schulz@qt.io> | 2024-02-16 06:28:19 +0000 |
commit | 2ceb1e2ad7be8b4d6ec2856da97530723cde7eb8 (patch) | |
tree | 86576b15a754e0ea0127f675bc24c2edfcd254a9 /src/libs/3rdparty/syntax-highlighting/src | |
parent | f1f4422dcea46d982feaca177c7b18aedd79486a (diff) |
TextEditor: update ksyntaxhighlighting engine to v5.249.0
Task-number: QTCREATORBUG-22558
Change-Id: I0f75fd00828992df37f596148fac98069794248e
Reviewed-by: Christian Stenger <christian.stenger@qt.io>
Diffstat (limited to 'src/libs/3rdparty/syntax-highlighting/src')
53 files changed, 1230 insertions, 907 deletions
diff --git a/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt index 419b8ed5b7..9d53a5bc3d 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt +++ b/src/libs/3rdparty/syntax-highlighting/src/CMakeLists.txt @@ -1,9 +1,9 @@ add_subdirectory(indexer) -if(TARGET Qt${QT_MAJOR_VERSION}::Gui) +if(TARGET Qt6::Gui) add_subdirectory(lib) add_subdirectory(cli) endif() -if(TARGET Qt${QT_MAJOR_VERSION}::Quick) +if(TARGET Qt6::Quick) add_subdirectory(quick) endif() diff --git a/src/libs/3rdparty/syntax-highlighting/src/Messages.sh b/src/libs/3rdparty/syntax-highlighting/src/Messages.sh index 6fb605ddf0..4024d9b74d 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/Messages.sh +++ b/src/libs/3rdparty/syntax-highlighting/src/Messages.sh @@ -8,4 +8,4 @@ sed -i -e 's/^i18nc/QT_TRANSLATE_NOOP/' rc.cpp grep --no-filename '"name"' ../data/themes/*.theme | \ sed -r -e 's/"name"/QT_TRANSLATE_NOOP("Theme", /; s/://; s/,?$/);/' >> rc.cpp -$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/syntaxhighlighting5_qt.pot +$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/syntaxhighlighting6_qt.pot diff --git a/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt index 1a4d24d828..3aa3a8afc3 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt +++ b/src/libs/3rdparty/syntax-highlighting/src/cli/CMakeLists.txt @@ -1,5 +1,5 @@ -add_executable(kate-syntax-highlighter kate-syntax-highlighter.cpp) -ecm_mark_nongui_executable(kate-syntax-highlighter) -target_link_libraries(kate-syntax-highlighter KF5SyntaxHighlighting) +add_executable(ksyntaxhighlighter6 ksyntaxhighlighter.cpp) +ecm_mark_nongui_executable(ksyntaxhighlighter6) +target_link_libraries(ksyntaxhighlighter6 KF6SyntaxHighlighting) -install(TARGETS kate-syntax-highlighter ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS ksyntaxhighlighter6 ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp index 5ce90e0053..681410cb70 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/cli/kate-syntax-highlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/cli/ksyntaxhighlighter.cpp @@ -26,7 +26,6 @@ static void applyHighlighter(Highlighter &highlighter, QCommandLineParser &parser, bool fromFileName, const QString &inFileName, - const QCommandLineOption &stdinOption, const QCommandLineOption &outputName, const Ts &...highlightParams) { @@ -38,30 +37,36 @@ static void applyHighlighter(Highlighter &highlighter, if (fromFileName) { highlighter.highlightFile(inFileName, highlightParams...); - } else if (parser.isSet(stdinOption)) { + } else { QFile inFile; inFile.open(stdin, QIODevice::ReadOnly); highlighter.highlightData(&inFile, highlightParams...); - } else { - parser.showHelp(1); } } +static Theme theme(const Repository &repo, const QString &themeName, Repository::DefaultTheme t) +{ + if (themeName.isEmpty()) { + return repo.defaultTheme(t); + } + return repo.theme(themeName); +} + int main(int argc, char **argv) { QCoreApplication app(argc, argv); - QCoreApplication::setApplicationName(QStringLiteral("kate-syntax-highlighter")); + QCoreApplication::setApplicationName(QStringLiteral("ksyntaxhighlighter")); QCoreApplication::setOrganizationDomain(QStringLiteral("kde.org")); QCoreApplication::setOrganizationName(QStringLiteral("KDE")); - QCoreApplication::setApplicationVersion(QStringLiteral(SyntaxHighlighting_VERSION_STRING)); - - Repository repo; + QCoreApplication::setApplicationVersion(QStringLiteral(KSYNTAXHIGHLIGHTING_VERSION_STRING)); QCommandLineParser parser; - parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using Kate syntax definitions.")); + parser.setApplicationDescription(app.translate("SyntaxHighlightingCLI", "Command line syntax highlighter using KSyntaxHighlighting syntax definitions.")); parser.addHelpOption(); parser.addVersionOption(); - parser.addPositionalArgument(app.translate("SyntaxHighlightingCLI", "source"), app.translate("SyntaxHighlightingCLI", "The source file to highlight.")); + parser.addPositionalArgument( + app.translate("SyntaxHighlightingCLI", "source"), + app.translate("SyntaxHighlightingCLI", "The source file to highlight. If absent, read the file from stdin and the --syntax option must be used.")); QCommandLineOption listDefs(QStringList() << QStringLiteral("l") << QStringLiteral("list"), app.translate("SyntaxHighlightingCLI", "List all available syntax definitions.")); @@ -85,8 +90,7 @@ int main(int argc, char **argv) QCommandLineOption themeName(QStringList() << QStringLiteral("t") << QStringLiteral("theme"), app.translate("SyntaxHighlightingCLI", "Color theme to use for highlighting."), - app.translate("SyntaxHighlightingCLI", "theme"), - repo.defaultTheme(Repository::LightTheme).name()); + app.translate("SyntaxHighlightingCLI", "theme")); parser.addOption(themeName); QCommandLineOption outputFormatOption( @@ -99,7 +103,7 @@ int main(int argc, char **argv) QCommandLineOption traceOption(QStringList() << QStringLiteral("syntax-trace"), app.translate("SyntaxHighlightingCLI", "Add information to debug a syntax file. Only works with --output-format=ansi or ansi256Colors. Possible " - "values are format, region, context and stackSize."), + "values are format, region, context, stackSize and all."), app.translate("SyntaxHighlightingCLI", "type")); parser.addOption(traceOption); @@ -107,18 +111,20 @@ int main(int argc, char **argv) app.translate("SyntaxHighlightingCLI", "Disable ANSI background for the default color.")); parser.addOption(noAnsiEditorBg); + QCommandLineOption unbufferedAnsi(QStringList() << QStringLiteral("U") << QStringLiteral("unbuffered"), + app.translate("SyntaxHighlightingCLI", "For ansi and ansi256Colors formats, flush the output buffer on each line.")); + parser.addOption(unbufferedAnsi); + QCommandLineOption titleOption( QStringList() << QStringLiteral("T") << QStringLiteral("title"), - app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"Kate Syntax Highlighter\" if reading from stdin)."), + app.translate("SyntaxHighlightingCLI", "Set HTML page's title\n(default: the filename or \"KSyntaxHighlighter\" if reading from stdin)."), app.translate("SyntaxHighlightingCLI", "title")); parser.addOption(titleOption); - QCommandLineOption stdinOption(QStringList() << QStringLiteral("stdin"), - app.translate("SyntaxHighlightingCLI", "Read file from stdin. The -s option must also be used.")); - parser.addOption(stdinOption); - parser.process(app); + Repository repo; + if (parser.isSet(listDefs)) { for (const auto &def : repo.definitions()) { std::cout << qPrintable(def.name()) << std::endl; @@ -177,7 +183,7 @@ int main(int argc, char **argv) return 1; } - QString outputFormat = parser.value(outputFormatOption); + const QString outputFormat = parser.value(outputFormatOption); if (0 == outputFormat.compare(QLatin1String("html"), Qt::CaseInsensitive)) { QString title; if (parser.isSet(titleOption)) { @@ -186,8 +192,8 @@ int main(int argc, char **argv) HtmlHighlighter highlighter; highlighter.setDefinition(def); - highlighter.setTheme(repo.theme(parser.value(themeName))); - applyHighlighter(highlighter, parser, fromFileName, inFileName, stdinOption, outputName, title); + highlighter.setTheme(theme(repo, parser.value(themeName), Repository::LightTheme)); + applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, title); } else { auto AnsiFormat = AnsiHighlighter::AnsiFormat::TrueColor; if (0 == outputFormat.compare(QLatin1String("ansi256Colors"), Qt::CaseInsensitive)) { @@ -197,18 +203,22 @@ int main(int argc, char **argv) return 2; } - auto debugOptions = AnsiHighlighter::TraceOptions(); + AnsiHighlighter::Options options{}; + options |= parser.isSet(noAnsiEditorBg) ? AnsiHighlighter::Option::NoOptions : AnsiHighlighter::Option::UseEditorBackground; + options |= parser.isSet(unbufferedAnsi) ? AnsiHighlighter::Option::Unbuffered : AnsiHighlighter::Option::NoOptions; if (parser.isSet(traceOption)) { - const auto options = parser.values(traceOption); - for (auto const &option : options) { + const auto traceOptions = parser.values(traceOption); + for (auto const &option : traceOptions) { if (option == QStringLiteral("format")) { - debugOptions |= AnsiHighlighter::TraceOption::Format; + options |= AnsiHighlighter::Option::TraceFormat; } else if (option == QStringLiteral("region")) { - debugOptions |= AnsiHighlighter::TraceOption::Region; + options |= AnsiHighlighter::Option::TraceRegion; } else if (option == QStringLiteral("context")) { - debugOptions |= AnsiHighlighter::TraceOption::Context; + options |= AnsiHighlighter::Option::TraceContext; } else if (option == QStringLiteral("stackSize")) { - debugOptions |= AnsiHighlighter::TraceOption::StackSize; + options |= AnsiHighlighter::Option::TraceStackSize; + } else if (option == QStringLiteral("all")) { + options |= AnsiHighlighter::Option::TraceAll; } else { std::cerr << "Unknown trace name." << std::endl; return 2; @@ -218,8 +228,8 @@ int main(int argc, char **argv) AnsiHighlighter highlighter; highlighter.setDefinition(def); - highlighter.setTheme(repo.theme(parser.value(themeName))); - applyHighlighter(highlighter, parser, fromFileName, inFileName, stdinOption, outputName, AnsiFormat, !parser.isSet(noAnsiEditorBg), debugOptions); + highlighter.setTheme(theme(repo, parser.value(themeName), Repository::DarkTheme)); + applyHighlighter(highlighter, parser, fromFileName, inFileName, outputName, AnsiFormat, options); } return 0; diff --git a/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt index 77a16faf22..cf6940b7bd 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt +++ b/src/libs/3rdparty/syntax-highlighting/src/indexer/CMakeLists.txt @@ -4,13 +4,8 @@ if(CMAKE_CROSSCOMPILING AND KATEHIGHLIGHTINGINDEXER_EXECUTABLE) add_executable(katehighlightingindexer IMPORTED GLOBAL) set_target_properties(katehighlightingindexer PROPERTIES IMPORTED_LOCATION ${KATEHIGHLIGHTINGINDEXER_EXECUTABLE}) elseif(CMAKE_CROSSCOMPILING) - if (NOT KF5_HOST_TOOLING) - message(FATAL_ERROR "Please provide a prefix with a native Qt build and pass -DKF5_HOST_TOOLING=path") - endif() - - # search native tooling prefix - string(FIND ${KF5_HOST_TOOLING} /lib idx) - string(SUBSTRING ${KF5_HOST_TOOLING} 0 ${idx} NATIVE_PREFIX) + include(ECMQueryQt) + ecm_query_qt(NATIVE_PREFIX QT_HOST_PREFIX) message(STATUS "Building katehighlightingindexer against ${NATIVE_PREFIX}") include(ExternalProject) @@ -19,7 +14,7 @@ elseif(CMAKE_CROSSCOMPILING) CMAKE_ARGS -DKSYNTAXHIGHLIGHTING_USE_GUI=OFF -DECM_DIR=${ECM_DIR} -DCMAKE_PREFIX_PATH=${NATIVE_PREFIX} -DCMAKE_INSTALL_PREFIX=${CMAKE_CURRENT_BINARY_DIR} - -DQT_MAJOR_VERSION=${QT_MAJOR_VERSION} + -DQT_MAJOR_VERSION=6 INSTALL_COMMAND "" BUILD_BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/native_katehighlightingindexer-prefix/src/native_katehighlightingindexer-build/bin/katehighlightingindexer ) @@ -31,9 +26,11 @@ else() # host build add_executable(katehighlightingindexer katehighlightingindexer.cpp ../lib/worddelimiters.cpp) ecm_mark_nongui_executable(katehighlightingindexer) - if(Qt5XmlPatterns_FOUND AND NOT ECM_ENABLE_SANITIZERS) - target_link_libraries(katehighlightingindexer Qt5::XmlPatterns) + if(XercesC_FOUND) + add_definitions(-DHAS_XERCESC) + kde_target_enable_exceptions(katehighlightingindexer PRIVATE) + target_link_libraries(katehighlightingindexer Qt6::Core XercesC::XercesC) else() - target_link_libraries(katehighlightingindexer Qt${QT_MAJOR_VERSION}::Core) + target_link_libraries(katehighlightingindexer Qt6::Core) endif() endif() diff --git a/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp b/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp index 9c541ef1b4..787747e21c 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/indexer/katehighlightingindexer.cpp @@ -12,12 +12,167 @@ #include <QFileInfo> #include <QMutableMapIterator> #include <QRegularExpression> +#include <QScopeGuard> #include <QVariant> #include <QXmlStreamReader> -#ifdef QT_XMLPATTERNS_LIB -#include <QXmlSchema> -#include <QXmlSchemaValidator> +#ifdef HAS_XERCESC + +#include <xercesc/framework/XMLGrammarPoolImpl.hpp> + +#include <xercesc/parsers/SAX2XMLReaderImpl.hpp> + +#include <xercesc/sax/ErrorHandler.hpp> +#include <xercesc/sax/SAXParseException.hpp> + +#include <xercesc/util/PlatformUtils.hpp> +#include <xercesc/util/XMLString.hpp> +#include <xercesc/util/XMLUni.hpp> + +#include <xercesc/framework/XMLGrammarPoolImpl.hpp> +#include <xercesc/validators/common/Grammar.hpp> + +using namespace xercesc; + +/* + * Ideas taken from: + * + * author : Boris Kolpackov <boris@codesynthesis.com> + * copyright : not copyrighted - public domain + * + * This program uses Xerces-C++ SAX2 parser to load a set of schema files + * and then to validate a set of XML documents against these schemas. To + * build this program you will need Xerces-C++ 3.0.0 or later. For more + * information, see: + * + * http://www.codesynthesis.com/~boris/blog/2010/03/15/validating-external-schemas-xerces-cxx/ + */ + +/** + * Error handler object used during xml schema validation. + */ +class CustomErrorHandler : public ErrorHandler +{ +public: + /** + * Constructor + * @param messages Pointer to the error message string to fill. + */ + CustomErrorHandler(QString *messages) + : m_messages(messages) + { + } + + /** + * Check global success/fail state. + * @return True if there was a failure, false otherwise. + */ + bool failed() const + { + return m_failed; + } + +private: + /** + * Severity classes for error messages. + */ + enum severity { s_warning, s_error, s_fatal }; + + /** + * Wrapper for warning exceptions. + * @param e Exception to handle. + */ + void warning(const SAXParseException &e) override + { + m_failed = true; // be strict, warnings are evil, too! + handle(e, s_warning); + } + + /** + * Wrapper for error exceptions. + * @param e Exception to handle. + */ + void error(const SAXParseException &e) override + { + m_failed = true; + handle(e, s_error); + } + + /** + * Wrapper for fatal error exceptions. + * @param e Exception to handle. + */ + void fatalError(const SAXParseException &e) override + { + m_failed = true; + handle(e, s_fatal); + } + + /** + * Reset the error status to "no error". + */ + void resetErrors() override + { + m_failed = false; + } + + /** + * Generic handler for error/warning/fatal error message exceptions. + * @param e Exception to handle. + * @param s Enum value encoding the message severtity. + */ + void handle(const SAXParseException &e, severity s) + { + // get id to print + const XMLCh *xid(e.getPublicId()); + if (!xid) + xid = e.getSystemId(); + + m_messages << QString::fromUtf16(xid) << ":" << e.getLineNumber() << ":" << e.getColumnNumber() << " " << (s == s_warning ? "warning: " : "error: ") + << QString::fromUtf16(e.getMessage()) << Qt::endl; + } + +private: + /** + * Storage for created error messages in this handler. + */ + QTextStream m_messages; + + /** + * Global error state. True if there was an error, false otherwise. + */ + bool m_failed = false; +}; + +void init_parser(SAX2XMLReaderImpl &parser) +{ + // Commonly useful configuration. + // + parser.setFeature(XMLUni::fgSAX2CoreNameSpaces, true); + parser.setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true); + parser.setFeature(XMLUni::fgSAX2CoreValidation, true); + + // Enable validation. + // + parser.setFeature(XMLUni::fgXercesSchema, true); + parser.setFeature(XMLUni::fgXercesSchemaFullChecking, true); + parser.setFeature(XMLUni::fgXercesValidationErrorAsFatal, true); + + // Use the loaded grammar during parsing. + // + parser.setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true); + + // Don't load schemas from any other source (e.g., from XML document's + // xsi:schemaLocation attributes). + // + parser.setFeature(XMLUni::fgXercesLoadSchema, false); + + // Xerces-C++ 3.1.0 is the first version with working multi import + // support. + // + parser.setFeature(XMLUni::fgXercesHandleMultipleImports, true); +} + #endif #include "../lib/worddelimiters_p.h" @@ -152,12 +307,10 @@ public: success = false; } - QSet<const Keywords *> referencedKeywords; QSet<ItemDatas::Style> usedAttributeNames; QSet<ItemDatas::Style> ignoredAttributeNames; - success = checkKeywordsList(definition, referencedKeywords) && success; - success = - checkContexts(definition, referencedKeywords, usedAttributeNames, ignoredAttributeNames, usedContexts, unreachableIncludedRules) && success; + success = checkKeywordsList(definition) && success; + success = checkContexts(definition, usedAttributeNames, ignoredAttributeNames, usedContexts, unreachableIncludedRules) && success; // search for non-existing itemDatas. const auto invalidNames = usedAttributeNames - definition.itemDatas.styleNames; @@ -362,7 +515,7 @@ private: QString content; int line; - friend uint qHash(const Item &item, uint seed = 0) + friend size_t qHash(const Item &item, size_t seed = 0) { return qHash(item.content, seed); } @@ -373,7 +526,7 @@ private: } }; - QVector<Item> keywords; + QList<Item> keywords; QSet<Item> includes; bool parseElement(const QString &filename, QXmlStreamReader &xml) @@ -486,7 +639,7 @@ private: QString weakDeliminator; // rules included by IncludeRules (without IncludeRule) - QVector<const Rule *> includedRules; + QList<const Rule *> includedRules; // IncludeRules included by IncludeRules QSet<const Rule *> includedIncludeRules; @@ -683,9 +836,10 @@ private: ContextName lineEndContext; ContextName lineEmptyContext; ContextName fallthroughContext; - QVector<Rule> rules; + QList<Rule> rules; XmlBool dynamic{}; XmlBool fallthrough{}; + XmlBool stopEmptyLineContextSwitchLoop{}; bool parseElement(const QString &filename, QXmlStreamReader &xml) { @@ -697,12 +851,17 @@ private: Parser parser{filename, xml, attr, success}; XmlBool noIndentationBasedFolding{}; - const bool isExtracted = parser.extractString(name, QStringLiteral("name")) || parser.extractString(attribute, QStringLiteral("attribute")) + // clang-format off + const bool isExtracted = parser.extractString(name, QStringLiteral("name")) + || parser.extractString(attribute, QStringLiteral("attribute")) || parser.extractString(lineEndContext.name, QStringLiteral("lineEndContext")) || parser.extractString(lineEmptyContext.name, QStringLiteral("lineEmptyContext")) || parser.extractString(fallthroughContext.name, QStringLiteral("fallthroughContext")) - || parser.extractXmlBool(dynamic, QStringLiteral("dynamic")) || parser.extractXmlBool(fallthrough, QStringLiteral("fallthrough")) + || parser.extractXmlBool(dynamic, QStringLiteral("dynamic")) + || parser.extractXmlBool(fallthrough, QStringLiteral("fallthrough")) + || parser.extractXmlBool(stopEmptyLineContextSwitchLoop, QStringLiteral("stopEmptyLineContextSwitchLoop")) || parser.extractXmlBool(noIndentationBasedFolding, QStringLiteral("noIndentationBasedFolding")); + // clang-format on success = parser.checkIfExtracted(isExtracted); } @@ -717,11 +876,6 @@ private: success = false; } - if (lineEndContext.name.isEmpty()) { - qWarning() << filename << "line" << xml.lineNumber() << "missing attribute: lineEndContext"; - success = false; - } - return success; } }; @@ -747,7 +901,7 @@ private: QString name; int line; - friend uint qHash(const Style &style, uint seed = 0) + friend size_t qHash(const Style &style, size_t seed = 0) { return qHash(style.name, seed); } @@ -802,7 +956,6 @@ private: const Context *firstContext = nullptr; QString filename; WordDelimiters wordDelimiters; - XmlBool casesensitive{}; Version kateVersion{}; QString kateVersionStr; QString languageName; @@ -865,7 +1018,7 @@ private: void resolveIncludeRules() { QSet<const Context *> usedContexts; - QVector<const Context *> contexts; + QList<const Context *> contexts; QMutableMapIterator<QString, Definition> def(m_definitions); while (def.hasNext()) { @@ -936,7 +1089,7 @@ private: QSet<const Context *> extractUsedContexts() const { QSet<const Context *> usedContexts; - QVector<const Context *> contexts; + QList<const Context *> contexts; QMapIterator<QString, Definition> def(m_definitions); while (def.hasNext()) { @@ -982,13 +1135,12 @@ private: }; struct IncludedRuleUnreachableBy { - QVector<RuleAndInclude> unreachableBy; + QList<RuleAndInclude> unreachableBy; bool alwaysUnreachable = true; }; //! Check contexts and rules bool checkContexts(const Definition &definition, - QSet<const Keywords *> &referencedKeywords, QSet<ItemDatas::Style> &usedAttributeNames, QSet<ItemDatas::Style> &ignoredAttributeNames, const QSet<const Context *> &usedContexts, @@ -1018,7 +1170,7 @@ private: usedAttributeNames.insert({context.attribute, context.line}); } - success = checkfallthrough(definition, context) && success; + success = checkContextAttribute(definition, context) && success; success = checkUreachableRules(definition.filename, context, unreachableIncludedRules) && success; success = suggestRuleMerger(definition.filename, context) && success; @@ -1032,7 +1184,7 @@ private: } success = checkLookAhead(rule) && success; success = checkStringDetect(rule) && success; - success = checkKeyword(definition, rule, referencedKeywords) && success; + success = checkKeyword(definition, rule) && success; success = checkRegExpr(filename, rule, context) && success; success = checkDelimiters(definition, rule) && success; } @@ -1047,12 +1199,13 @@ private: //! - dynamic=true but no place holder used? //! - is not . with lookAhead="1" //! - is not ^... without column ou firstNonSpace attribute - //! - is not equivalent to DetectSpaces, DetectChar, Detect2Chars, StringDetect, DetectIdentifier, RangeDetect + //! - is not equivalent to DetectSpaces, DetectChar, Detect2Chars, StringDetect, DetectIdentifier, RangeDetect, LineContinue or AnyChar //! - has no unused captures //! - has no unnecessary quantifier with lookAhead bool checkRegExpr(const QString &filename, const Context::Rule &rule, const Context &context) const { - if (rule.type == Context::Rule::Type::RegExpr) { + // ignore empty regex because the error is raised during xml parsing + if (rule.type == Context::Rule::Type::RegExpr && !rule.string.isEmpty()) { const QRegularExpression regexp(rule.string); if (!checkRegularExpression(rule.filename, regexp, rule.line)) { return false; @@ -1092,13 +1245,21 @@ private: // is RangeDetect static const QRegularExpression isRange(QStringLiteral("^\\^?" REG_CHAR "(?:" - "\\.\\*[?*]?" REG_CHAR "|" - "\\[\\^(" REG_ESCAPE_CHAR "|.)\\]\\*[?*]?\\1" + "\\.\\*[?+]?" REG_CHAR "|" + "\\[\\^(" REG_ESCAPE_CHAR "|.)\\]\\*[?+]?\\1" ")$")); if ((rule.lookAhead == XmlBool::True || rule.minimal == XmlBool::True || rule.string.contains(QStringLiteral(".*?")) || rule.string.contains(QStringLiteral("[^"))) && reg.contains(isRange)) { - qWarning() << filename << "line" << rule.line << "RegExpr should be replaced by RangeDetect:" << rule.string; + qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by RangeDetect:" << rule.string; + return false; + } + + // is AnyChar + static const QRegularExpression isAnyChar(QStringLiteral(R"(^(\^|\((\?:)?)*\[(?!\^)[-\]]?(\\[^0BDPSWbdpswoux]|[^-\]\\])*\]\)*$)")); + if (rule.string.contains(isAnyChar)) { + auto extra = (reg[0] == QLatin1Char('^') || reg[1] == QLatin1Char('^')) ? "with column=\"0\"" : ""; + qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by AnyChar:" << rule.string << extra; return false; } @@ -1106,7 +1267,7 @@ private: static const QRegularExpression isLineContinue(QStringLiteral("^\\^?" REG_CHAR "\\$$")); if (reg.contains(isLineContinue)) { auto extra = (reg[0] == QLatin1Char('^')) ? "with column=\"0\"" : ""; - qWarning() << filename << "line" << rule.line << "RegExpr should be replaced by LineContinue:" << rule.string << extra; + qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by LineContinue:" << rule.string << extra; return false; } @@ -1124,7 +1285,7 @@ private: if (rule.lookAhead == XmlBool::True && rule.minimal != XmlBool::True && reg.contains(isMinimal) && !reg.contains(hasNotGreedy) && (!rule.context.context || !rule.context.context->hasDynamicRule || regexp.captureCount() == 0) && (reg.back() != QLatin1Char('$') || reg.contains(QLatin1Char('|')))) { - qWarning() << filename << "line" << rule.line + qWarning() << rule.filename << "line" << rule.line << "RegExpr should be have minimal=\"1\" or use lazy operator (i.g, '.*' -> '.*?'):" << rule.string; return false; } @@ -1160,8 +1321,8 @@ private: return false; } - // column="0" or firstNonSpace="1" - if (rule.column == -1 && rule.firstNonSpace != XmlBool::True) { + // column="0" + if (rule.column == -1) { // ^ without | // (^sas*) -> ok // (^sa|s*) -> ko @@ -1204,7 +1365,7 @@ private: } if (replace) { - qWarning() << rule.filename << "line" << rule.line << "column=\"0\" or firstNonSpace=\"1\" missing with RegExpr:" << rule.string; + qWarning() << rule.filename << "line" << rule.line << "column=\"0\" missing with RegExpr:" << rule.string; return false; } } @@ -1306,11 +1467,8 @@ private: if (!useCapture) { // is DetectIdentifier - static const QRegularExpression isInsensitiveDetectIdentifier( - QStringLiteral(R"(^(\((\?:)?)?\[((a-z|_){2}|(A-Z|_){2})\]([+][*?]?)?\[((0-9|a-z|_){3}|(0-9|A-Z|_){3})\][*][*?]?(\))?$)")); - static const QRegularExpression isSensitiveDetectIdentifier( - QStringLiteral(R"(^(\((\?:)?)?\[(a-z|A-Z|_){3}\]([+][*?]?)?\[(0-9|a-z|A-Z|_){4}\][*][*?]?(\))?$)")); - auto &isDetectIdentifier = (rule.insensitive == XmlBool::True) ? isInsensitiveDetectIdentifier : isSensitiveDetectIdentifier; + static const QRegularExpression isDetectIdentifier( + QStringLiteral(R"(^(\((\?:)?|\^)*\[(\\p\{L\}|_){2}\]([+][?+]?)?\[(\\p\{N\}|\\p\{L\}|_){3}\][*][?+]?\)*$)")); if (rule.string.contains(isDetectIdentifier)) { qWarning() << rule.filename << "line" << rule.line << "RegExpr should be replaced by DetectIdentifier:" << rule.string; return false; @@ -1357,7 +1515,7 @@ private: static const QRegularExpression unnecessaryQuantifier2(QStringLiteral(R"([*+?]([.][*+?]{0,2})?[)]*$)")); auto &unnecessaryQuantifier = useCapture ? unnecessaryQuantifier1 : unnecessaryQuantifier2; if (rule.lookAhead == XmlBool::True && rule.minimal != XmlBool::True && reg.contains(unnecessaryQuantifier)) { - qWarning() << filename << "line" << rule.line + qWarning() << rule.filename << "line" << rule.line << "Last quantifier is not necessary (i.g., 'xyz*' -> 'xy', 'xyz+.' -> 'xyz.'):" << rule.string; return false; } @@ -1418,19 +1576,13 @@ private: return true; } - //! Search for rules with lookAhead="true" and context="#stay". - //! This would cause an infinite loop. - bool checkfallthrough(const Definition &definition, const Context &context) const + //! Check fallthrough and fallthroughContext. + //! Check kateversion for stopEmptyLineContextSwitchLoop. + bool checkContextAttribute(const Definition &definition, const Context &context) const { bool success = true; if (!context.fallthroughContext.name.isEmpty()) { - if (context.fallthroughContext.stay) { - qWarning() << definition.filename << "line" << context.line << "possible infinite loop due to fallthroughContext=\"#stay\" in context " - << context.name; - success = false; - } - const bool mandatoryFallthroughAttribute = definition.kateVersion < Version{5, 62}; if (context.fallthrough == XmlBool::True && !mandatoryFallthroughAttribute) { qWarning() << definition.filename << "line" << context.line << "fallthrough attribute is unnecessary with kateversion >= 5.62 in context" @@ -1444,6 +1596,12 @@ private: } } + if (context.stopEmptyLineContextSwitchLoop != XmlBool::Unspecified && definition.kateVersion < Version{5, 103}) { + qWarning() << definition.filename << "line" << context.line + << "stopEmptyLineContextSwitchLoop attribute is only valid with kateversion >= 5.103 in context" << context.name; + success = false; + } + return success; } @@ -1478,15 +1636,12 @@ private: return false; } - //! Search for rules with lookAhead="true" and context="#stay". - //! This would cause an infinite loop. - bool checkKeyword(const Definition &definition, const Context::Rule &rule, QSet<const Keywords *> &referencedKeywords) const + //! Check that keyword rule reference an existing keyword list. + bool checkKeyword(const Definition &definition, const Context::Rule &rule) const { if (rule.type == Context::Rule::Type::keyword) { auto it = definition.keywordsList.find(rule.string); - if (it != definition.keywordsList.end()) { - referencedKeywords.insert(&*it); - } else { + if (it == definition.keywordsList.end()) { qWarning() << rule.filename << "line" << rule.line << "reference of non-existing keyword list:" << rule.string; return false; } @@ -1504,13 +1659,7 @@ private: return true; } - //! Check that StringDetect contains more that 2 characters - //! Fix with following command: - //! \code - //! sed -E - //! '/StringDetect/{/dynamic="(1|true)|insensitive="(1|true)/!{s/StringDetect(.*)String="(.|<|>|"|&)(.|<|>|"|&)"/Detect2Chars\1char="\2" - //! char1="\3"/;t;s/StringDetect(.*)String="(.|<|>|"|&)"/DetectChar\1char="\2"/}}' -i file.xml... - //! \endcode + //! Check that StringDetect contains a placeHolder when dynamic="1" bool checkStringDetect(const Context::Rule &rule) const { if (rule.type == Context::Rule::Type::StringDetect) { @@ -1527,7 +1676,7 @@ private: } //! Check \<include> and delimiter in a keyword list - bool checkKeywordsList(const Definition &definition, QSet<const Keywords *> &referencedKeywords) const + bool checkKeywordsList(const Definition &definition) const { bool success = true; @@ -1542,7 +1691,7 @@ private: << "<include> is only available since version \"5.53\". Please, increase kateversion."; success = false; } - success = checkKeywordInclude(definition, include, referencedKeywords) && success; + success = checkKeywordInclude(definition, include) && success; } // Check that keyword list items do not have deliminator character @@ -1562,16 +1711,13 @@ private: } //! Search for non-existing keyword include. - bool checkKeywordInclude(const Definition &definition, const Keywords::Items::Item &include, QSet<const Keywords *> &referencedKeywords) const + bool checkKeywordInclude(const Definition &definition, const Keywords::Items::Item &include) const { bool containsKeywordName = true; int const idx = include.content.indexOf(QStringLiteral("##")); if (idx == -1) { auto it = definition.keywordsList.find(include.content); containsKeywordName = (it != definition.keywordsList.end()); - if (containsKeywordName) { - referencedKeywords.insert(&*it); - } } else { auto defName = include.content.mid(idx + 2); auto listName = include.content.left(idx); @@ -1644,10 +1790,10 @@ private: } /// Search RuleAndInclude associated with the characters of @p s. - /// \return an empty QVector when at least one character is not found. - QVector<RuleAndInclude> find(QStringView s) const + /// \return an empty QList when at least one character is not found. + QList<RuleAndInclude> find(QStringView s) const { - QVector<RuleAndInclude> result; + QList<RuleAndInclude> result; for (QChar c : s) { if (!find(c)) { @@ -1731,8 +1877,8 @@ private: } /// Search RuleAndInclude associated with the characters of @p s. - /// \return an empty QVector when at least one character is not found. - QVector<RuleAndInclude> find(QStringView s) const + /// \return an empty QList when at least one character is not found. + QList<RuleAndInclude> find(QStringView s) const { for (int i = 0; i < m_size; ++i) { auto result = m_charTables[i]->find(s); @@ -1743,7 +1889,7 @@ private: return result; } } - return QVector<RuleAndInclude>(); + return QList<RuleAndInclude>(); } /// Associates @p c with a rule. @@ -1785,7 +1931,7 @@ private: // Iterates over all the rules, including those in includedRules struct RuleIterator { - RuleIterator(const QVector<ObservableRule> &rules, const ObservableRule &endRule) + RuleIterator(const QList<ObservableRule> &rules, const ObservableRule &endRule) : m_end(&endRule - rules.data()) , m_rules(rules) { @@ -1830,10 +1976,10 @@ private: private: int m_i = 0; - int m_i2; - int m_end; - const QVector<ObservableRule> &m_rules; - const QVector<const Context::Rule *> *m_includedRules = nullptr; + int m_i2 = 0; + const int m_end; + const QList<ObservableRule> &m_rules; + const QList<const Context::Rule *> *m_includedRules = nullptr; }; // Dot regex container that satisfies firstNonSpace and column. @@ -1915,7 +2061,7 @@ private: DotRegex dotRegex; - QVector<ObservableRule> observedRules; + QList<ObservableRule> observedRules; observedRules.reserve(context.rules.size()); for (const Context::Rule &rule : context.rules) { const Context::Rule *includeRule = nullptr; @@ -1937,7 +2083,7 @@ private: for (auto &observedRule : observedRules) { const Context::Rule &rule = *observedRule.rule; bool isUnreachable = false; - QVector<RuleAndInclude> unreachableBy; + QList<RuleAndInclude> unreachableBy; // declare rule as unreachable if ruleAndInclude is not empty auto updateUnreachable1 = [&](RuleAndInclude ruleAndInclude) { @@ -1948,7 +2094,7 @@ private: }; // declare rule as unreachable if ruleAndIncludes is not empty - auto updateUnreachable2 = [&](const QVector<RuleAndInclude> &ruleAndIncludes) { + auto updateUnreachable2 = [&](const QList<RuleAndInclude> &ruleAndIncludes) { if (!ruleAndIncludes.isEmpty()) { isUnreachable = true; unreachableBy.append(ruleAndIncludes); @@ -2030,6 +2176,8 @@ private: case Context::Rule::Type::Float: updateUnreachable2(CharTableArray(detectChars, rule).find(QStringLiteral("0123456789."))); updateUnreachable1(floatRule.setRule(rule)); + // check that Float is before Int + updateUnreachable1(Rule4(intRule).setRule(rule)); break; // check if hidden by another DetectIdentifier rule @@ -2629,12 +2777,32 @@ int main(int argc, char *argv[]) return 1; } -#ifdef QT_XMLPATTERNS_LIB - // open schema - QXmlSchema schema; - if (!schema.load(QUrl::fromLocalFile(app.arguments().at(2)))) { +#ifdef HAS_XERCESC + // care for proper init and cleanup + XMLPlatformUtils::Initialize(); + auto cleanup = qScopeGuard(XMLPlatformUtils::Terminate); + + /* + * parse XSD first time and cache it + */ + XMLGrammarPoolImpl xsd(XMLPlatformUtils::fgMemoryManager); + + // create parser for the XSD + SAX2XMLReaderImpl parser(XMLPlatformUtils::fgMemoryManager, &xsd); + init_parser(parser); + QString messages; + CustomErrorHandler eh(&messages); + parser.setErrorHandler(&eh); + + // load grammar into the pool, on error just abort + const auto xsdFile = app.arguments().at(2); + if (!parser.loadGrammar((const char16_t *)xsdFile.utf16(), Grammar::SchemaGrammarType, true) || eh.failed()) { + qWarning("Failed to parse XSD %s: %s", qPrintable(xsdFile), qPrintable(messages)); return 2; } + + // lock the pool, no later modifications wanted! + xsd.lockPool(); #endif const QString hlFilenamesListing = app.arguments().value(3); @@ -2665,10 +2833,20 @@ int main(int argc, char *argv[]) continue; } -#ifdef QT_XMLPATTERNS_LIB - // validate against schema - QXmlSchemaValidator validator(schema); - if (!validator.validate(&hlFile, QUrl::fromLocalFile(hlFile.fileName()))) { +#ifdef HAS_XERCESC + // create parser + SAX2XMLReaderImpl parser(XMLPlatformUtils::fgMemoryManager, &xsd); + init_parser(parser); + QString messages; + CustomErrorHandler eh(&messages); + parser.setErrorHandler(&eh); + + // parse the XML file + parser.parse((const char16_t *)hlFile.fileName().utf16()); + + // report issues + if (eh.failed()) { + qWarning("Failed to validate XML %s: %s", qPrintable(hlFile.fileName()), qPrintable(messages)); anyError = 4; continue; } @@ -2708,6 +2886,10 @@ int main(int argc, char *argv[]) // add boolean one hl[QStringLiteral("hidden")] = attrToBool(xml.attributes().value(QLatin1String("hidden"))); + // keep some strings as UTF-8 for faster translations + hl[QStringLiteral("nameUtf8")] = hl[QStringLiteral("name")].toString().toUtf8(); + hl[QStringLiteral("sectionUtf8")] = hl[QStringLiteral("section")].toString().toUtf8(); + // remember hl hls[QFileInfo(hlFile).fileName()] = hl; diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt index fe89fdd715..2b2845eba4 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/CMakeLists.txt @@ -1,8 +1,14 @@ -add_library(KF5SyntaxHighlighting) +add_library(KF6SyntaxHighlighting) -ecm_create_qm_loader(syntax_highlighting_QM_LOADER syntaxhighlighting5_qt) +set_target_properties(KF6SyntaxHighlighting PROPERTIES + VERSION ${KSYNTAXHIGHLIGHTING_VERSION} + SOVERSION ${KSYNTAXHIGHLIGHTING_SOVERSION} + EXPORT_NAME SyntaxHighlighting +) + +ecm_create_qm_loader(syntax_highlighting_QM_LOADER syntaxhighlighting6_qt) -target_sources(KF5SyntaxHighlighting PRIVATE +target_sources(KF6SyntaxHighlighting PRIVATE abstracthighlighter.cpp context.cpp contextswitch.cpp @@ -25,7 +31,7 @@ target_sources(KF5SyntaxHighlighting PRIVATE ${syntax_highlighting_QM_LOADER} $<TARGET_OBJECTS:SyntaxHighlightingData> ) -ecm_qt_declare_logging_category(KF5SyntaxHighlighting +ecm_qt_declare_logging_category(KF6SyntaxHighlighting HEADER ksyntaxhighlighting_logging.h IDENTIFIER KSyntaxHighlighting::Log CATEGORY_NAME kf.syntaxhighlighting @@ -34,25 +40,21 @@ ecm_qt_declare_logging_category(KF5SyntaxHighlighting EXPORT KSYNTAXHIGHLIGHTING ) -ecm_generate_export_header(KF5SyntaxHighlighting +ecm_generate_export_header(KF6SyntaxHighlighting BASE_NAME KSyntaxHighlighting GROUP_BASE_NAME KF VERSION ${KF_VERSION} + USE_VERSION_HEADER DEPRECATED_BASE_VERSION 0 - DEPRECATION_VERSIONS 5.87 + DEPRECATION_VERSIONS EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT} ) -set_target_properties(KF5SyntaxHighlighting PROPERTIES - VERSION ${SyntaxHighlighting_VERSION} - SOVERSION ${SyntaxHighlighting_SOVERSION} - EXPORT_NAME SyntaxHighlighting -) -target_link_libraries(KF5SyntaxHighlighting +target_link_libraries(KF6SyntaxHighlighting PUBLIC - Qt${QT_MAJOR_VERSION}::Gui + Qt6::Gui PRIVATE - Qt${QT_MAJOR_VERSION}::Network + Qt6::Network ) set(Forwarding_Header_Names @@ -74,12 +76,12 @@ ecm_generate_headers(CamelCase_HEADERS OUTPUT_DIR ${CMAKE_BINARY_DIR}/KSyntaxHighlighting/KSyntaxHighlighting ) -target_include_directories(KF5SyntaxHighlighting +target_include_directories(KF6SyntaxHighlighting INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KSyntaxHighlighting>" PUBLIC "$<BUILD_INTERFACE:${CMAKE_BINARY_DIR}/KSyntaxHighlighting;>" ) -install(TARGETS KF5SyntaxHighlighting EXPORT KF5SyntaxHighlightingTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) +install(TARGETS KF6SyntaxHighlighting EXPORT KF6SyntaxHighlightingTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) install(FILES ${CamelCase_HEADERS} @@ -90,17 +92,17 @@ install(FILES if(BUILD_QCH) ecm_add_qch( - KF5SyntaxHighlighting_QCH + KF6SyntaxHighlighting_QCH NAME KSyntaxHighlighting - BASE_NAME KF5SyntaxHighlighting + BASE_NAME KF6SyntaxHighlighting VERSION ${KF_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 + Qt6Core_QCH + Qt6Gui_QCH INCLUDE_DIRS ${CMAKE_CURRENT_BINARY_DIR} BLANK_MACROS @@ -111,11 +113,3 @@ if(BUILD_QCH) 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_KF}/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 index ac5d98abfb..87dabadc7b 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.cpp @@ -12,6 +12,7 @@ #include "format.h" #include "ksyntaxhighlighting_logging.h" #include "repository.h" +#include "repository_p.h" #include "rule_p.h" #include "state.h" #include "state_p.h" @@ -97,13 +98,6 @@ static inline int firstNonSpaceChar(QStringView text) return text.size(); } -#if KSYNTAXHIGHLIGHTING_BUILD_DEPRECATED_SINCE(5, 87) -State AbstractHighlighter::highlightLine(const QString &text, const State &state) -{ - return highlightLine(QStringView(text), state); -} -#endif - State AbstractHighlighter::highlightLine(QStringView text, const State &state) { Q_D(AbstractHighlighter); @@ -116,43 +110,47 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) return State(); } + // limit the cache for unification to some reasonable size + // we use here at the moment 64k elements to not hog too much memory + // and to make the clearing no big stall + if (defData->unify.size() > 64 * 1024) + defData->unify.clear(); + // verify/initialize state auto newState = state; auto stateData = StateData::get(newState); - const auto definitionId = DefinitionData::get(d->m_definition)->id; - if (!stateData->isEmpty() && stateData->m_defId != definitionId) { + bool isSharedData = true; + if (Q_UNLIKELY(stateData && stateData->m_defId != defData->id)) { qCDebug(Log) << "Got invalid state, resetting."; - stateData->clear(); + stateData = nullptr; } - if (stateData->isEmpty()) { + if (Q_UNLIKELY(!stateData)) { + stateData = StateData::reset(newState); stateData->push(defData->initialContext(), QStringList()); - stateData->m_defId = definitionId; + stateData->m_defId = defData->id; + isSharedData = false; } // process empty lines - if (text.isEmpty()) { + if (Q_UNLIKELY(text.isEmpty())) { /** * handle line empty context switches * guard against endless loops * see https://phabricator.kde.org/D18509 */ int endlessLoopingCounter = 0; - while (!stateData->topContext()->lineEmptyContext().isStay() || !stateData->topContext()->lineEndContext().isStay()) { + while (!stateData->topContext()->lineEmptyContext().isStay()) { /** * line empty context switches */ - if (!stateData->topContext()->lineEmptyContext().isStay()) { - if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList())) { - /** - * end when trying to #pop the main context - */ - break; - } + if (!d->switchContext(stateData, stateData->topContext()->lineEmptyContext(), QStringList(), newState, isSharedData)) { /** - * line end context switches only when lineEmptyContext is #stay. This avoids - * skipping empty lines after a line continuation character (see bug 405903) + * end when trying to #pop the main context */ - } else if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList())) { + break; + } + + if (stateData->topContext()->stopEmptyLineContextSwitchLoop()) { break; } @@ -165,9 +163,11 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) } auto context = stateData->topContext(); applyFormat(0, 0, context->attributeFormat()); - return newState; + return *defData->unify.insert(newState); } + auto &dynamicRegexpCache = RepositoryPrivate::get(defData->repo)->m_dynamicRegexpCache; + int offset = 0; int beginOffset = 0; bool lineContinuation = false; @@ -178,10 +178,10 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) * - store the result of the first position that matches (or -1 for no match in the full line) in the skipOffsets hash for re-use * - have capturesForLastDynamicSkipOffset as guard for dynamic regexes to invalidate the cache if they might have changed */ - QVarLengthArray<QPair<Rule *, int>, 8> skipOffsets; + QVarLengthArray<QPair<const Rule *, int>, 8> skipOffsets; QStringList capturesForLastDynamicSkipOffset; - auto getSkipOffsetValue = [&skipOffsets](Rule *r) -> int { + auto getSkipOffsetValue = [&skipOffsets](const Rule *r) -> int { auto i = std::find_if(skipOffsets.begin(), skipOffsets.end(), [r](const auto &v) { return v.first == r; }); @@ -190,7 +190,7 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) return i->second; }; - auto insertSkipOffset = [&skipOffsets](Rule *r, int i) { + auto insertSkipOffset = [&skipOffsets](const Rule *r, int i) { auto it = std::find_if(skipOffsets.begin(), skipOffsets.end(), [r](const auto &v) { return v.first == r; }); @@ -237,7 +237,8 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) bool isLookAhead = false; int newOffset = 0; const Format *newFormat = nullptr; - for (const auto &rule : stateData->topContext()->rules()) { + for (const auto &ruleShared : stateData->topContext()->rules()) { + auto rule = ruleShared.get(); /** * filter out rules that require a specific column */ @@ -265,29 +266,33 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) } } - /** - * 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 - * - * we need to invalidate this if we are dynamic and have different captures then last time - */ - if (rule->isDynamic() && (capturesForLastDynamicSkipOffset != stateData->topCaptures())) { - skipOffsets.clear(); - } - const auto currentSkipOffset = getSkipOffsetValue(rule.get()); - if (currentSkipOffset < 0 || currentSkipOffset > offset) { - continue; + int currentSkipOffset = 0; + if (Q_UNLIKELY(rule->hasSkipOffset())) { + /** + * 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 + * + * we need to invalidate this if we are dynamic and have different captures then last time + */ + if (rule->isDynamic() && (capturesForLastDynamicSkipOffset != stateData->topCaptures())) { + skipOffsets.clear(); + } else { + currentSkipOffset = getSkipOffsetValue(rule); + if (currentSkipOffset < 0 || currentSkipOffset > offset) { + continue; + } + } } - const auto newResult = rule->doMatch(text, offset, stateData->topCaptures()); + auto newResult = rule->doMatch(text, offset, stateData->topCaptures(), dynamicRegexpCache); 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) { - insertSkipOffset(rule.get(), newResult.skipOffset()); + insertSkipOffset(rule, newResult.skipOffset()); // remember new captures, if dynamic to enforce proper reset above on change! if (rule->isDynamic()) { @@ -316,12 +321,12 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) if (rule->isLookAhead()) { Q_ASSERT(!rule->context().isStay()); - d->switchContext(stateData, rule->context(), newResult.captures()); + d->switchContext(stateData, rule->context(), std::move(newResult.captures()), newState, isSharedData); isLookAhead = true; break; } - d->switchContext(stateData, rule->context(), newResult.captures()); + d->switchContext(stateData, rule->context(), std::move(newResult.captures()), newState, isSharedData); newFormat = rule->attributeFormat().isValid() ? &rule->attributeFormat() : &stateData->topContext()->attributeFormat(); if (newOffset == text.size() && rule->isLineContinue()) { lineContinuation = true; @@ -334,7 +339,7 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) if (newOffset <= offset) { // no matching rule if (stateData->topContext()->fallthrough()) { - d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList()); + d->switchContext(stateData, stateData->topContext()->fallthroughContext(), QStringList(), newState, isSharedData); continue; } @@ -381,7 +386,7 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) { int endlessLoopingCounter = 0; while (!stateData->topContext()->lineEndContext().isStay() && !lineContinuation) { - if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList())) { + if (!d->switchContext(stateData, stateData->topContext()->lineEndContext(), QStringList(), newState, isSharedData)) { break; } @@ -394,18 +399,30 @@ State AbstractHighlighter::highlightLine(QStringView text, const State &state) } } - return newState; + return *defData->unify.insert(newState); } -bool AbstractHighlighterPrivate::switchContext(StateData *data, const ContextSwitch &contextSwitch, const QStringList &captures) +bool AbstractHighlighterPrivate::switchContext(StateData *&data, const ContextSwitch &contextSwitch, QStringList &&captures, State &state, bool &isSharedData) { + const auto popCount = contextSwitch.popCount(); + const auto context = contextSwitch.context(); + if (popCount <= 0 && !context) { + return true; + } + + // a modified state must be detached before modification + if (isSharedData) { + data = StateData::detach(state); + isSharedData = false; + } + // kill as many items as requested from the stack, will always keep the initial context alive! - const bool initialContextSurvived = data->pop(contextSwitch.popCount()); + const bool initialContextSurvived = data->pop(popCount); // if we have a new context to add, push it // then we always "succeed" - if (contextSwitch.context()) { - data->push(contextSwitch.context(), captures); + if (context) { + data->push(context, std::move(captures)); return true; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h index 49cfbf2530..676a0f522a 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter.h @@ -7,20 +7,15 @@ #ifndef KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H #define KSYNTAXHIGHLIGHTING_ABSTRACTHIGHLIGHTERM_H +#include "definition.h" #include "ksyntaxhighlighting_export.h" #include <QObject> - -#include <memory> - -QT_BEGIN_NAMESPACE -class QString; -QT_END_NAMESPACE +#include <QStringView> namespace KSyntaxHighlighting { class AbstractHighlighterPrivate; -class Definition; class FoldingRegion; class Format; class State; @@ -106,21 +101,8 @@ public: protected: AbstractHighlighter(); - AbstractHighlighter(AbstractHighlighterPrivate *dd); + KSYNTAXHIGHLIGHTING_NO_EXPORT explicit AbstractHighlighter(AbstractHighlighterPrivate *dd); -#if KSYNTAXHIGHLIGHTING_ENABLE_DEPRECATED_SINCE(5, 87) - /** - * @copydoc highlightLine(QStringView,const State&) - * @deprecated since 5.87, use highlightLine(QStringView, const State&) instead. - */ - // no deprecation warning, as removal of this will automatically "port" the using code - State highlightLine(const QString &text, const State &state); -#endif - - // 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() diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h index 6128beccfa..04ac9898f8 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/abstracthighlighter_p.h @@ -14,6 +14,7 @@ namespace KSyntaxHighlighting { class ContextSwitch; class StateData; +class State; class AbstractHighlighterPrivate { @@ -22,7 +23,7 @@ public: virtual ~AbstractHighlighterPrivate(); void ensureDefinitionLoaded(); - bool switchContext(StateData *data, const ContextSwitch &contextSwitch, const QStringList &captures); + bool switchContext(StateData *&data, const ContextSwitch &contextSwitch, QStringList &&captures, State &state, bool &isSharedData); Definition m_definition; Theme m_theme; diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp index 31678018f9..e9bea02d1c 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.cpp @@ -5,6 +5,7 @@ */ #include "ansihighlighter.h" +#include "abstracthighlighter_p.h" #include "context_p.h" #include "definition.h" #include "definition_p.h" @@ -18,6 +19,7 @@ #include <QFile> #include <QFileInfo> #include <QHash> +#include <QIODevice> #include <QTextStream> #include <cmath> @@ -783,7 +785,7 @@ GraphLine &lineAtOffset(std::vector<GraphLine> &graphLines, int offset) } // disable bold, italic and underline on | -const QLatin1String graphSymbol("\x1b[21;23;24m|"); +const QLatin1String graphSymbol("\x1b[22;23;24m|"); // reverse video const QLatin1String nameStyle("\x1b[7m"); @@ -793,8 +795,8 @@ const QLatin1String nameStyle("\x1b[7m"); class DebugSyntaxHighlighter : public KSyntaxHighlighting::AbstractHighlighter { public: - using TraceOption = KSyntaxHighlighting::AnsiHighlighter::TraceOption; - using TraceOptions = KSyntaxHighlighting::AnsiHighlighter::TraceOptions; + using Option = KSyntaxHighlighting::AnsiHighlighter::Option; + using Options = KSyntaxHighlighting::AnsiHighlighter::Options; void setDefinition(const KSyntaxHighlighting::Definition &def) override { @@ -815,14 +817,14 @@ public: QLatin1String infoStyle, QLatin1String editorBackground, const std::vector<QPair<QString, QString>> &ansiStyles, - TraceOptions traceOptions) + Options options) { initRegionStyles(ansiStyles); - m_hasFormatTrace = traceOptions.testFlag(TraceOption::Format); - m_hasRegionTrace = traceOptions.testFlag(TraceOption::Region); - m_hasStackSizeTrace = traceOptions.testFlag(TraceOption::StackSize); - m_hasContextTrace = traceOptions.testFlag(TraceOption::Context); + m_hasFormatTrace = options.testFlag(Option::TraceFormat); + m_hasRegionTrace = options.testFlag(Option::TraceRegion); + m_hasStackSizeTrace = options.testFlag(Option::TraceStackSize); + m_hasContextTrace = options.testFlag(Option::TraceContext); const bool hasFormatOrContextTrace = m_hasFormatTrace || m_hasContextTrace || m_hasStackSizeTrace; const bool hasSeparator = hasFormatOrContextTrace && m_hasRegionTrace; @@ -831,6 +833,7 @@ public: bool firstLine = true; State state; QString currentLine; + const bool isUnbuffered = options.testFlag(Option::Unbuffered); while (in.readLineInto(¤tLine)) { auto oldState = state; state = highlightLine(currentLine, state); @@ -864,6 +867,10 @@ public: } m_highlightedFragments.clear(); + + if (isUnbuffered) { + out.flush(); + } } } @@ -970,12 +977,14 @@ private: QString label; if (m_hasStackSizeTrace) { - label += QLatin1Char('(') % QString::number(stateData->size()) % QLatin1Char(')'); + // first state is empty + int stateSize = stateData ? stateData->size() : 0; + label = QLatin1Char('(') % QString::number(stateSize) % QLatin1Char(')'); } if (m_hasContextTrace) { // first state is empty - if (stateData->isEmpty()) { + if (!stateData) { return label + QStringLiteral("[???]"); } @@ -1138,7 +1147,7 @@ private: QString name; int offset; int length; - quint16 formatId; + int formatId; }; struct ContextCaptureHighlighter : KSyntaxHighlighting::AbstractHighlighter { @@ -1168,7 +1177,7 @@ private: int depth; int offset; int bindIndex; - quint16 regionId; + int regionId; State state; }; @@ -1190,7 +1199,7 @@ private: }; } // anonymous namespace -class KSyntaxHighlighting::AnsiHighlighterPrivate +class KSyntaxHighlighting::AnsiHighlighterPrivate : public AbstractHighlighterPrivate { public: QTextStream out; @@ -1201,7 +1210,7 @@ public: }; AnsiHighlighter::AnsiHighlighter() - : d(new AnsiHighlighterPrivate()) + : AbstractHighlighter(new AnsiHighlighterPrivate()) { } @@ -1209,6 +1218,7 @@ AnsiHighlighter::~AnsiHighlighter() = default; void AnsiHighlighter::setOutputFile(const QString &fileName) { + Q_D(AnsiHighlighter); if (d->file.isOpen()) { d->file.close(); } @@ -1218,21 +1228,16 @@ void AnsiHighlighter::setOutputFile(const QString &fileName) return; } d->out.setDevice(&d->file); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - d->out.setCodec("UTF-8"); -#endif } void AnsiHighlighter::setOutputFile(FILE *fileHandle) { + Q_D(AnsiHighlighter); d->file.open(fileHandle, QIODevice::WriteOnly); d->out.setDevice(&d->file); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - d->out.setCodec("UTF-8"); -#endif } -void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, bool useEditorBackground, TraceOptions traceOptions) +void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, Options options) { QFileInfo fi(fileName); QFile f(fileName); @@ -1241,19 +1246,21 @@ void AnsiHighlighter::highlightFile(const QString &fileName, AnsiFormat format, return; } - highlightData(&f, format, useEditorBackground, traceOptions); + highlightData(&f, format, options); } -void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useEditorBackground, TraceOptions traceOptions) +void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, Options options) { + Q_D(AnsiHighlighter); + if (!d->out.device()) { qCWarning(Log) << "No output stream defined!"; return; } const auto is256Colors = (format == AnsiFormat::XTerm256Color); - const auto theme = this->theme(); - const auto definition = this->definition(); + const auto &theme = d->m_theme; + const auto &definition = d->m_definition; auto definitions = definition.includedDefinitions(); definitions.append(definition); @@ -1265,6 +1272,8 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE QLatin1String foregroundDefaultColor; QLatin1String backgroundDefaultColor; + const bool useEditorBackground = options.testFlag(Option::UseEditorBackground); + // https://en.wikipedia.org/wiki/ANSI_escape_code#SGR_parameters if (useEditorBackground) { @@ -1277,23 +1286,19 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE backgroundDefaultColor = backgroundColorBuffer.latin1().mid(2); } - // ansiStyles must not be empty for applyFormat to work even with a definition without any context - if (d->ansiStyles.empty()) { - d->ansiStyles.resize(32); - } else { - d->ansiStyles[0].first.clear(); - d->ansiStyles[0].second.clear(); + int maxId = 0; + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { + maxId = qMax(maxId, format.id()); + } } + d->ansiStyles.clear(); + // ansiStyles must not be empty for applyFormat to work even with a definition without any context + d->ansiStyles.resize(maxId + 1); // initialize ansiStyles - for (auto &&definition : std::as_const(definitions)) { - for (auto &&format : std::as_const(DefinitionData::get(definition)->formats)) { - const auto id = format.id(); - if (id >= d->ansiStyles.size()) { - // better than id + 1 to avoid successive allocations - d->ansiStyles.resize(id * 2); - } - + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { AnsiBuffer buffer; buffer.append(QLatin1String("\x1b[")); @@ -1329,7 +1334,8 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE // if there is ANSI style if (buffer.latin1().size() > 2) { buffer.setFinalStyle(); - d->ansiStyles[id].first = buffer.latin1(); + auto &style = d->ansiStyles[format.id()]; + style.first = buffer.latin1(); if (useEditorBackground) { buffer.clear(); @@ -1338,11 +1344,11 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE buffer.append(hasEffect ? QLatin1String("\x1b[0;") : QLatin1String("\x1b[")); buffer.append(backgroundDefaultColor); buffer.setFinalStyle(); - d->ansiStyles[id].second = buffer.latin1(); + style.second = buffer.latin1(); } else if (hasEffect) { buffer.append(QLatin1String("\x1b[")); if (hasBold) { - buffer.append(QLatin1String("21;")); + buffer.append(QLatin1String("22;")); } if (hasItalic) { buffer.append(QLatin1String("23;")); @@ -1354,10 +1360,10 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE buffer.append(QLatin1String("29;")); } buffer.setFinalStyle(); - d->ansiStyles[id].second = buffer.latin1(); + style.second = buffer.latin1(); } } else { - d->ansiStyles[id].second = QStringLiteral("\x1b[0m"); + style.second = QStringLiteral("\x1b[0m"); } } } @@ -1370,13 +1376,11 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE } QTextStream in(dev); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - in.setCodec("UTF-8"); -#endif - if (!traceOptions) { + if (!options.testAnyFlag(Option::TraceAll)) { State state; QString currentLine; + const bool isUnbuffered = options.testFlag(Option::Unbuffered); while (in.readLineInto(¤tLine)) { d->currentLine = currentLine; state = highlightLine(d->currentLine, state); @@ -1386,6 +1390,10 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE } else { d->out << QLatin1Char('\n'); } + + if (isUnbuffered) { + d->out.flush(); + } } } else { AnsiBuffer buffer; @@ -1394,7 +1402,7 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE buffer.setFinalStyle(); DebugSyntaxHighlighter debugHighlighter; debugHighlighter.setDefinition(definition); - debugHighlighter.highlightData(in, d->out, buffer.latin1(), backgroundDefaultColor, d->ansiStyles, traceOptions); + debugHighlighter.highlightData(in, d->out, buffer.latin1(), backgroundDefaultColor, d->ansiStyles, options); } if (useEditorBackground) { @@ -1408,6 +1416,7 @@ void AnsiHighlighter::highlightData(QIODevice *dev, AnsiFormat format, bool useE void AnsiHighlighter::applyFormat(int offset, int length, const Format &format) { + Q_D(AnsiHighlighter); auto const &ansiStyle = d->ansiStyles[format.id()]; d->out << ansiStyle.first << d->currentLine.mid(offset, length) << ansiStyle.second; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h index ffb13f38f3..0942aa0242 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/ansihighlighter.h @@ -11,10 +11,11 @@ #include "ksyntaxhighlighting_export.h" #include <QFlags> -#include <QIODevice> #include <QString> -#include <memory> +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE namespace KSyntaxHighlighting { @@ -29,24 +30,25 @@ public: XTerm256Color, }; - enum class TraceOption { + enum class Option { NoOptions, - Format = 1 << 0, - Region = 1 << 1, - Context = 1 << 2, - StackSize = 1 << 3, + UseEditorBackground = 1 << 0, + Unbuffered = 1 << 1, + + // Options that displays a useful visual aid for syntax creation + TraceFormat = 1 << 2, + TraceRegion = 1 << 3, + TraceContext = 1 << 4, + TraceStackSize = 1 << 5, + TraceAll = TraceFormat | TraceRegion | TraceContext | TraceStackSize, }; - Q_DECLARE_FLAGS(TraceOptions, TraceOption) + Q_DECLARE_FLAGS(Options, Option) AnsiHighlighter(); ~AnsiHighlighter() override; - void highlightFile(const QString &fileName, - AnsiFormat format = AnsiFormat::TrueColor, - bool useEditorBackground = true, - TraceOptions traceOptions = TraceOptions()); - void - highlightData(QIODevice *device, AnsiFormat format = AnsiFormat::TrueColor, bool useEditorBackground = true, TraceOptions traceOptions = TraceOptions()); + void highlightFile(const QString &fileName, AnsiFormat format = AnsiFormat::TrueColor, Options options = Option::UseEditorBackground); + void highlightData(QIODevice *device, AnsiFormat format = AnsiFormat::TrueColor, Options options = Option::UseEditorBackground); void setOutputFile(const QString &fileName); void setOutputFile(FILE *fileHandle); @@ -55,10 +57,10 @@ protected: void applyFormat(int offset, int length, const Format &format) override; private: - std::unique_ptr<AnsiHighlighterPrivate> d; + Q_DECLARE_PRIVATE(AnsiHighlighter) }; } -Q_DECLARE_OPERATORS_FOR_FLAGS(KSyntaxHighlighting::AnsiHighlighter::TraceOptions) +Q_DECLARE_OPERATORS_FOR_FLAGS(KSyntaxHighlighting::AnsiHighlighter::Options) #endif // KSYNTAXHIGHLIGHTING_ANSIHIGHLIGHTER_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp index ff5254cb6e..af269d14d0 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context.cpp @@ -38,7 +38,15 @@ void Context::resolveContexts(DefinitionData &def, const HighlightingContextData m_lineEndContext.resolve(def, data.lineEndContext); m_lineEmptyContext.resolve(def, data.lineEmptyContext); m_fallthroughContext.resolve(def, data.fallthroughContext); - m_fallthrough = !m_fallthroughContext.isStay(); + m_stopEmptyLineContextSwitchLoop = data.stopEmptyLineContextSwitchLoop; + + /** + * line end context switches only when lineEmptyContext is #stay. This avoids + * skipping empty lines after a line continuation character (see bug 405903) + */ + if (m_lineEmptyContext.isStay()) { + m_lineEmptyContext = m_lineEndContext; + } m_rules.reserve(data.rules.size()); for (const auto &ruleData : data.rules) { @@ -65,6 +73,7 @@ void Context::resolveIncludes(DefinitionData &def) for (auto it = m_rules.begin(); it != m_rules.end();) { const IncludeRules *includeRules = it->get()->castToIncludeRules(); if (!includeRules) { + m_hasDynamicRule = m_hasDynamicRule || it->get()->isDynamic(); ++it; continue; } @@ -111,6 +120,8 @@ void Context::resolveIncludes(DefinitionData &def) context->resolveIncludes(*defData); } + m_hasDynamicRule = m_hasDynamicRule || context->m_hasDynamicRule; + /** * handle included attribute * transitive closure: we might include attributes included from somewhere else diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h index 7e077b5a24..8cf0f1bfab 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/context_p.h @@ -16,10 +16,6 @@ #include <vector> -QT_BEGIN_NAMESPACE -class QXmlStreamReader; -QT_END_NAMESPACE - namespace KSyntaxHighlighting { class DefinitionData; @@ -52,7 +48,17 @@ public: bool fallthrough() const { - return m_fallthrough; + return !m_fallthroughContext.isStay(); + } + + bool hasDynamicRule() const + { + return m_hasDynamicRule; + } + + bool stopEmptyLineContextSwitchLoop() const + { + return m_stopEmptyLineContextSwitchLoop; } const ContextSwitch &fallthroughContext() const @@ -96,7 +102,8 @@ private: Format m_attributeFormat; ResolveState m_resolveState = Unresolved; - bool m_fallthrough = false; + bool m_hasDynamicRule = false; + bool m_stopEmptyLineContextSwitchLoop = true; bool m_indentationBasedFolding; }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp index 670dfddedb..e2cca6da71 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.cpp @@ -26,8 +26,6 @@ #include <QCborMap> #include <QCoreApplication> #include <QFile> -#include <QHash> -#include <QStringList> #include <QXmlStreamReader> #include <algorithm> @@ -43,13 +41,8 @@ DefinitionData::DefinitionData() DefinitionData::~DefinitionData() = default; -DefinitionData *DefinitionData::get(const Definition &def) -{ - return def.d.get(); -} - Definition::Definition() - : d(new DefinitionData) + : d(std::make_shared<DefinitionData>()) { d->q = *this; } @@ -63,6 +56,9 @@ Definition &Definition::operator=(const Definition &) = default; Definition::Definition(std::shared_ptr<DefinitionData> &&dd) : d(std::move(dd)) { + if (!d) { + Definition().d.swap(d); + } } bool Definition::operator==(const Definition &other) const @@ -92,7 +88,10 @@ QString Definition::name() const QString Definition::translatedName() const { - return QCoreApplication::instance()->translate("Language", d->name.toUtf8().constData()); + if (d->translatedName.isEmpty()) { + d->translatedName = QCoreApplication::instance()->translate("Language", d->nameUtf8.isEmpty() ? d->name.toUtf8().constData() : d->nameUtf8.constData()); + } + return d->translatedName; } QString Definition::section() const @@ -102,15 +101,19 @@ QString Definition::section() const QString Definition::translatedSection() const { - return QCoreApplication::instance()->translate("Language Section", d->section.toUtf8().constData()); + if (d->translatedSection.isEmpty()) { + d->translatedSection = QCoreApplication::instance()->translate("Language Section", + d->sectionUtf8.isEmpty() ? d->section.toUtf8().constData() : d->sectionUtf8.constData()); + } + return d->translatedSection; } -QVector<QString> Definition::mimeTypes() const +QList<QString> Definition::mimeTypes() const { return d->mimetypes; } -QVector<QString> Definition::extensions() const +QList<QString> Definition::extensions() const { return d->extensions; } @@ -218,12 +221,12 @@ bool Definition::setKeywordList(const QString &name, const QStringList &content) } } -QVector<Format> Definition::formats() const +QList<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()); + auto formatList = d->formats.values(); std::sort(formatList.begin(), formatList.end(), [](const KSyntaxHighlighting::Format &lhs, const KSyntaxHighlighting::Format &rhs) { return lhs.id() < rhs.id(); }); @@ -231,13 +234,13 @@ QVector<Format> Definition::formats() const return formatList; } -QVector<Definition> Definition::includedDefinitions() const +QList<Definition> Definition::includedDefinitions() const { d->load(); // init worklist and result used as guard with this definition - QVector<const DefinitionData *> queue{d.get()}; - QVector<Definition> definitions{*this}; + QList<const DefinitionData *> queue{d.get()}; + QList<Definition> definitions{*this}; while (!queue.empty()) { const auto *def = queue.back(); queue.pop_back(); @@ -275,7 +278,7 @@ QPair<QString, QString> Definition::multiLineCommentMarker() const return {d->multiLineCommentStartMarker, d->multiLineCommentEndMarker}; } -QVector<QPair<QChar, QString>> Definition::characterEncodings() const +QList<QPair<QChar, QString>> Definition::characterEncodings() const { d->load(); return d->characterEncodings; @@ -394,7 +397,11 @@ void DefinitionData::clear() characterEncodings.clear(); fileName.clear(); + nameUtf8.clear(); + translatedName.clear(); section.clear(); + sectionUtf8.clear(); + translatedSection.clear(); style.clear(); indenter.clear(); author.clear(); @@ -405,6 +412,9 @@ void DefinitionData::clear() version = 0.0f; priority = 0; hidden = false; + + // purge our cache that is used to unify states + unify.clear(); } bool DefinitionData::loadMetaData(const QString &definitionFileName) @@ -433,7 +443,9 @@ bool DefinitionData::loadMetaData(const QString &definitionFileName) bool DefinitionData::loadMetaData(const QString &file, const QCborMap &obj) { name = obj.value(QLatin1String("name")).toString(); + nameUtf8 = obj.value(QLatin1String("name")).toByteArray(); section = obj.value(QLatin1String("section")).toString(); + sectionUtf8 = obj.value(QLatin1String("section")).toByteArray(); version = obj.value(QLatin1String("version")).toInteger(); priority = obj.value(QLatin1String("priority")).toInteger(); style = obj.value(QLatin1String("style")).toString(); @@ -444,13 +456,10 @@ bool DefinitionData::loadMetaData(const QString &file, const QCborMap &obj) fileName = file; const auto exts = obj.value(QLatin1String("extensions")).toString(); - for (const auto &ext : exts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { - extensions.push_back(ext); - } + extensions = exts.split(QLatin1Char(';'), Qt::SkipEmptyParts); + const auto mts = obj.value(QLatin1String("mimetype")).toString(); - for (const auto &mt : mts.split(QLatin1Char(';'), Qt::SkipEmptyParts)) { - mimetypes.push_back(mt); - } + mimetypes = mts.split(QLatin1Char(';'), Qt::SkipEmptyParts); return true; } @@ -805,10 +814,10 @@ bool DefinitionData::checkKateVersion(QStringView verStr) qCWarning(Log) << "Skipping" << fileName << "due to having no valid kateversion attribute:" << verStr; return false; } - const auto major = verStr.left(idx).toString().toInt(); - const auto minor = verStr.mid(idx + 1).toString().toInt(); + 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)) { + if (major > KSYNTAXHIGHLIGHTING_VERSION_MAJOR || (major == KSYNTAXHIGHLIGHTING_VERSION_MAJOR && minor > KSYNTAXHIGHLIGHTING_VERSION_MINOR)) { qCWarning(Log) << "Skipping" << fileName << "due to being too new, version:" << verStr; return false; } @@ -834,34 +843,20 @@ void DefinitionData::addImmediateIncludedDefinition(const Definition &def) DefinitionRef::DefinitionRef() = default; -DefinitionRef::DefinitionRef(const Definition &def) +DefinitionRef::DefinitionRef(const Definition &def) noexcept : d(def.d) { } -DefinitionRef::DefinitionRef(Definition &&def) - : d(std::move(def.d)) -{ -} - -DefinitionRef &DefinitionRef::operator=(const Definition &def) +DefinitionRef &DefinitionRef::operator=(const Definition &def) noexcept { d = def.d; return *this; } -DefinitionRef &DefinitionRef::operator=(Definition &&def) -{ - d = std::move(def.d); - return *this; -} - Definition DefinitionRef::definition() const { - if (!d.expired()) { - return Definition(d.lock()); - } - return Definition(); + return Definition(d.lock()); } bool DefinitionRef::operator==(const DefinitionRef &other) const @@ -873,3 +868,5 @@ bool DefinitionRef::operator==(const Definition &other) const { return !d.owner_before(other.d) && !other.d.owner_before(d); } + +#include "moc_definition.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h index 05757ea52a..e69492bee4 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition.h @@ -10,16 +10,12 @@ #include "ksyntaxhighlighting_export.h" +#include <QList> #include <QPair> -#include <QVector> +#include <QString> #include <memory> #include <qobjectdefs.h> -QT_BEGIN_NAMESPACE -class QChar; -class QString; -QT_END_NAMESPACE - namespace KSyntaxHighlighting { class Context; @@ -185,13 +181,13 @@ public: /** * Mime types associated with this syntax definition. */ - QVector<QString> mimeTypes() const; + QList<QString> mimeTypes() const; /** * File extensions associated with this syntax definition. * The returned list contains wildcards. */ - QVector<QString> extensions() const; + QList<QString> extensions() const; /** * Returns the definition version. @@ -360,7 +356,7 @@ public: * The order of the Format items equals the order of the itemDatas in the xml file. * @since 5.49 */ - QVector<Format> formats() const; + QList<Format> formats() const; /** * Returns a list of Definitions that are referenced with the IncludeRules rule. @@ -369,7 +365,7 @@ public: * * @since 5.49 */ - QVector<Definition> includedDefinitions() const; + QList<Definition> includedDefinitions() const; /** * Returns the marker that starts a single line comment. @@ -400,7 +396,7 @@ public: * the string \"{A} represents the character Ä. * @since 5.50 */ - QVector<QPair<QChar, QString>> characterEncodings() const; + QList<QPair<QChar, QString>> characterEncodings() const; /** * @} @@ -409,14 +405,14 @@ public: private: friend class DefinitionData; friend class DefinitionRef; - explicit Definition(std::shared_ptr<DefinitionData> &&dd); + KSYNTAXHIGHLIGHTING_NO_EXPORT explicit Definition(std::shared_ptr<DefinitionData> &&dd); std::shared_ptr<DefinitionData> d; }; } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Definition, Q_RELOCATABLE_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 index 542f255b32..ec00b31897 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definition_p.h @@ -10,11 +10,13 @@ #include "definitionref_p.h" #include "highlightingdata_p.hpp" +#include "state.h" #include "worddelimiters_p.h" #include <QHash> +#include <QList> +#include <QSet> #include <QString> -#include <QVector> #include <vector> @@ -36,7 +38,10 @@ public: DefinitionData(const DefinitionData &) = delete; DefinitionData &operator=(const DefinitionData &) = delete; - static DefinitionData *get(const Definition &def); + static DefinitionData *get(const Definition &def) + { + return def.d.get(); + } bool isLoaded() const; bool loadMetaData(const QString &definitionFileName); @@ -80,9 +85,9 @@ public: std::vector<Context> contexts; QHash<QString, Format> formats; // data loaded from xml file and emptied after loading contexts - QVector<HighlightingContextData> contextDatas; + QList<HighlightingContextData> contextDatas; // Definition referenced by IncludeRules and ContextSwitch - QVector<DefinitionRef> immediateIncludedDefinitions; + QList<DefinitionRef> immediateIncludedDefinitions; WordDelimiters wordDelimiters; WordDelimiters wordWrapDelimiters; bool keywordIsLoaded = false; @@ -93,21 +98,28 @@ public: CommentPosition singleLineCommentPosition = CommentPosition::StartOfLine; QString multiLineCommentStartMarker; QString multiLineCommentEndMarker; - QVector<QPair<QChar, QString>> characterEncodings; + QList<QPair<QChar, QString>> characterEncodings; QString fileName; QString name = QStringLiteral(QT_TRANSLATE_NOOP("Language", "None")); + QByteArray nameUtf8; + mutable QString translatedName; QString section; + QByteArray sectionUtf8; + mutable QString translatedSection; QString style; QString indenter; QString author; QString license; - QVector<QString> mimetypes; - QVector<QString> extensions; + QList<QString> mimetypes; + QList<QString> extensions; Qt::CaseSensitivity caseSensitive = Qt::CaseSensitive; int version = 0; int priority = 0; bool hidden = false; + + // cache that is used to unify states in AbstractHighlighter::highlightLine + mutable QSet<State> unify; }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp index c1335789dc..88ba5d7759 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitiondownloader.cpp @@ -169,8 +169,8 @@ 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"); + const QString url = QLatin1String("https://www.kate-editor.org/syntax/update-") + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MAJOR) + QLatin1Char('.') + + QString::number(KSYNTAXHIGHLIGHTING_VERSION_MINOR) + QLatin1String(".xml"); auto req = QNetworkRequest(QUrl(url)); req.setAttribute(QNetworkRequest::RedirectPolicyAttribute, QNetworkRequest::NoLessSafeRedirectPolicy); auto reply = d->nam->get(req); @@ -178,3 +178,5 @@ void DefinitionDownloader::start() d->definitionListDownloadFinished(reply); }); } + +#include "moc_definitiondownloader.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h index 0bd805624d..a7ef08f614 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/definitionref_p.h @@ -30,10 +30,8 @@ class DefinitionRef { public: DefinitionRef(); - explicit DefinitionRef(const Definition &def); - explicit DefinitionRef(Definition &&def); - DefinitionRef &operator=(const Definition &def); - DefinitionRef &operator=(Definition &&def); + explicit DefinitionRef(const Definition &def) noexcept; + DefinitionRef &operator=(const Definition &def) noexcept; Definition definition() const; diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h new file mode 100644 index 0000000000..dcef97a841 --- /dev/null +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/dynamicregexpcache_p.h @@ -0,0 +1,39 @@ +/* + SPDX-FileCopyrightText: 2023 Jonathan Poelen <jonathan.poelen+kde@gmail.com> + + SPDX-License-Identifier: MIT +*/ + +#ifndef KSYNTAXHIGHLIGHTING_DYNAMICREGEXPCACHE_P_H +#define KSYNTAXHIGHLIGHTING_DYNAMICREGEXPCACHE_P_H + +#include <QCache> +#include <QRegularExpression> +#include <QString> + +#include <utility> + +namespace KSyntaxHighlighting +{ + +class DynamicRegexpCache +{ +public: + const QRegularExpression &compileRegexp(QString &&pattern, QRegularExpression::PatternOptions patternOptions) + { + const auto key = std::pair{std::move(pattern), patternOptions}; + if (const auto regexp = m_cache.object(key)) { + return *regexp; + } + auto regexp = new QRegularExpression(key.first, patternOptions); + m_cache.insert(key, regexp); + return *regexp; + } + +private: + QCache<std::pair<QString, QRegularExpression::PatternOptions>, QRegularExpression> m_cache; +}; + +} + +#endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp index 3bca63eecd..9ed625b12e 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.cpp @@ -8,36 +8,39 @@ using namespace KSyntaxHighlighting; -static_assert(sizeof(FoldingRegion) == 2, "FoldingRegion is size-sensitive to frequent use in KTextEditor!"); +static_assert(sizeof(FoldingRegion) == sizeof(int), "FoldingRegion is size-sensitive to frequent use in KTextEditor!"); -FoldingRegion::FoldingRegion() - : m_type(None) - , m_id(0) -{ -} +FoldingRegion::FoldingRegion() = default; -FoldingRegion::FoldingRegion(Type type, quint16 id) - : m_type(type) - , m_id(id) +FoldingRegion::FoldingRegion(Type type, int id) + : m_idWithType((type == End) ? -id : id) { } bool FoldingRegion::operator==(const FoldingRegion &other) const { - return m_id == other.m_id && m_type == other.m_type; + return m_idWithType == other.m_idWithType; } bool FoldingRegion::isValid() const { - return type() != None; + return m_idWithType != 0; } -quint16 FoldingRegion::id() const +int FoldingRegion::id() const { - return m_id; + return (m_idWithType < 0) ? -m_idWithType : m_idWithType; } FoldingRegion::Type FoldingRegion::type() const { - return static_cast<FoldingRegion::Type>(m_type); + if (isValid()) { + return (m_idWithType < 0) ? End : Begin; + } + return None; +} + +FoldingRegion FoldingRegion::sibling() const +{ + return isValid() ? FoldingRegion(type() ? End : Begin, id()) : FoldingRegion(); } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h index ca4cacafb2..e2a9e1fc68 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/foldingregion.h @@ -22,7 +22,7 @@ public: * Defines whether a FoldingRegion starts or ends a folding region. */ enum Type { - //! Used internally as indicator for invalid FoldingRegion%s. + //! Used internally as indicator for an invalid FoldingRegion. None, //! Indicates the start of a FoldingRegion. Begin, @@ -64,7 +64,7 @@ public: * brace, you need to do kind of a reference counting to find the correct * closing brace. */ - quint16 id() const; + int id() const; /** * Returns whether this is the begin or end of a region. @@ -74,12 +74,21 @@ public: */ Type type() const; + /** + * Returns the matching start or end region. + * + * @note Will return invalid region for an invalid region. + * + * @since 6.0 + */ + FoldingRegion sibling() const; + private: friend class Rule; - FoldingRegion(Type type, quint16 id); + KSYNTAXHIGHLIGHTING_NO_EXPORT FoldingRegion(Type type, int id); - quint16 m_type : 2; - quint16 m_id : 14; + // 0 is invalid, positive begin, negative end + int m_idWithType = 0; }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp index 518a1e9ee9..2259cd3411 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.cpp @@ -98,7 +98,7 @@ QString Format::name() const return d->name; } -quint16 Format::id() const +int Format::id() const { return d->id; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/format.h b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h index 496a54e42c..397a1bab01 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/format.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format.h @@ -13,12 +13,6 @@ #include <QExplicitlySharedDataPointer> -QT_BEGIN_NAMESPACE -class QColor; -class QString; -class QXmlStreamReader; -QT_END_NAMESPACE - namespace KSyntaxHighlighting { class FormatPrivate; @@ -54,7 +48,7 @@ public: * the repository is reloaded (which also invalidatess the corresponding * Definition anyway). */ - quint16 id() const; + int id() const; /** Returns the underlying TextStyle of this Format. * Every Theme::TextStyle is visually defined by a Theme. A Format uses one @@ -192,7 +186,7 @@ private: } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Format, Q_RELOCATABLE_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 index d8770f1ef7..ea74531445 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/format_p.h @@ -13,6 +13,10 @@ #include <QSharedData> #include <QString> +QT_BEGIN_NAMESPACE +class QXmlStreamReader; +QT_END_NAMESPACE + namespace KSyntaxHighlighting { class FormatPrivate : public QSharedData @@ -21,6 +25,11 @@ public: FormatPrivate() = default; static FormatPrivate *detachAndGet(Format &format); + static std::intptr_t ptrId(const Format &format) + { + return std::intptr_t(format.d.data()); + } + TextStyleData styleOverride(const Theme &theme) const; void load(QXmlStreamReader &reader); @@ -33,7 +42,7 @@ public: QString name; TextStyleData style; Theme::TextStyle defaultStyle = Theme::Normal; - quint16 id = 0; + int id = 0; bool spellCheck = true; }; diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp index adb1c346c1..d95ad43b7f 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata.cpp @@ -379,6 +379,7 @@ void HighlightingContextData::load(const QString &defName, QXmlStreamReader &rea lineEmptyContext = reader.attributes().value(QLatin1String("lineEmptyContext")).toString(); fallthroughContext = reader.attributes().value(QLatin1String("fallthroughContext")).toString(); noIndentationBasedFolding = Xml::attrToBool(reader.attributes().value(QLatin1String("noIndentationBasedFolding"))); + stopEmptyLineContextSwitchLoop = Xml::attrToBool(reader.attributes().value(QLatin1String("stopEmptyLineContextSwitchLoop"))); rules.reserve(8); diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp index 80aeaf4934..f49227dbf9 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/highlightingdata_p.hpp @@ -208,6 +208,7 @@ public: std::vector<Rule> rules; + bool stopEmptyLineContextSwitchLoop = false; bool noIndentationBasedFolding = false; }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp index 1f68e33d3e..928ae149d1 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.cpp @@ -6,7 +6,9 @@ */ #include "htmlhighlighter.h" +#include "abstracthighlighter_p.h" #include "definition.h" +#include "definition_p.h" #include "format.h" #include "ksyntaxhighlighting_logging.h" #include "state.h" @@ -14,21 +16,22 @@ #include <QFile> #include <QFileInfo> +#include <QIODevice> #include <QTextStream> -#include <QVarLengthArray> using namespace KSyntaxHighlighting; -class KSyntaxHighlighting::HtmlHighlighterPrivate +class KSyntaxHighlighting::HtmlHighlighterPrivate : public AbstractHighlighterPrivate { public: std::unique_ptr<QTextStream> out; std::unique_ptr<QFile> file; QString currentLine; + std::vector<QString> htmlStyles; }; HtmlHighlighter::HtmlHighlighter() - : d(new HtmlHighlighterPrivate()) + : AbstractHighlighter(new HtmlHighlighterPrivate()) { } @@ -38,27 +41,21 @@ HtmlHighlighter::~HtmlHighlighter() void HtmlHighlighter::setOutputFile(const QString &fileName) { + Q_D(HtmlHighlighter); 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())); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) d->out->setEncoding(QStringConverter::Utf8); -#else - d->out->setCodec("UTF-8"); -#endif } void HtmlHighlighter::setOutputFile(FILE *fileHandle) { + Q_D(HtmlHighlighter); d->out.reset(new QTextStream(fileHandle, QIODevice::WriteOnly)); -#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) d->out->setEncoding(QStringConverter::Utf8); -#else - d->out->setCodec("UTF-8"); -#endif } void HtmlHighlighter::highlightFile(const QString &fileName, const QString &title) @@ -79,7 +76,7 @@ void HtmlHighlighter::highlightFile(const QString &fileName, const QString &titl /** * @brief toHtmlRgba - * Converts QColor -> rgba(r, g, b, a) if there is an alpha channel + * Converts QColor -> #RRGGBBAA if there is an alpha channel * otherwise it will just return the hexcode. This is because QColor * outputs #AARRGGBB, whereas browser support #RRGGBBAA. * @@ -91,22 +88,24 @@ static QString toHtmlRgbaString(const QColor &color) if (color.alpha() == 0xFF) { return color.name(); } - - QString rgba = QStringLiteral("rgba("); - rgba.append(QString::number(color.red())); - rgba.append(QLatin1Char(',')); - rgba.append(QString::number(color.green())); - rgba.append(QLatin1Char(',')); - rgba.append(QString::number(color.blue())); - rgba.append(QLatin1Char(',')); - // this must be alphaF - rgba.append(QString::number(color.alphaF())); - rgba.append(QLatin1Char(')')); - return rgba; + static const char16_t digits[]{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; + QChar hexcode[9]; + hexcode[0] = QLatin1Char('#'); + hexcode[1] = digits[color.red() >> 4]; + hexcode[2] = digits[color.red() & 0xf]; + hexcode[3] = digits[color.green() >> 4]; + hexcode[4] = digits[color.green() & 0xf]; + hexcode[5] = digits[color.blue() >> 4]; + hexcode[6] = digits[color.blue() & 0xf]; + hexcode[7] = digits[color.alpha() >> 4]; + hexcode[8] = digits[color.alpha() & 0xf]; + return QString(hexcode, 9); } void HtmlHighlighter::highlightData(QIODevice *dev, const QString &title) { + Q_D(HtmlHighlighter); + if (!d->out) { qCWarning(Log) << "No output stream defined!"; return; @@ -114,29 +113,73 @@ void HtmlHighlighter::highlightData(QIODevice *dev, const QString &title) QString htmlTitle; if (title.isEmpty()) { - htmlTitle = QStringLiteral("Kate Syntax Highlighter"); + htmlTitle = QStringLiteral("KSyntaxHighlighter"); } else { htmlTitle = title.toHtmlEscaped(); } + const auto &theme = d->m_theme; + const auto &definition = d->m_definition; + + auto definitions = definition.includedDefinitions(); + definitions.append(definition); + + int maxId = 0; + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { + maxId = qMax(maxId, format.id()); + } + } + d->htmlStyles.clear(); + // htmlStyles must not be empty for applyFormat to work even with a definition without any context + d->htmlStyles.resize(maxId + 1); + + // initialize htmlStyles + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { + auto &buffer = d->htmlStyles[format.id()]; + if (format.hasTextColor(theme)) { + buffer += QStringLiteral("color:") + toHtmlRgbaString(format.textColor(theme)) + QStringLiteral(";"); + } + if (format.hasBackgroundColor(theme)) { + buffer += QStringLiteral("background-color:") + toHtmlRgbaString(format.backgroundColor(theme)) + QStringLiteral(";"); + } + if (format.isBold(theme)) { + buffer += QStringLiteral("font-weight:bold;"); + } + if (format.isItalic(theme)) { + buffer += QStringLiteral("font-style:italic;"); + } + if (format.isUnderline(theme)) { + buffer += QStringLiteral("text-decoration:underline;"); + } + if (format.isStrikeThrough(theme)) { + buffer += QStringLiteral("text-decoration:line-through;"); + } + + if (!buffer.isEmpty()) { + buffer.insert(0, QStringLiteral("<span style=\"")); + // replace last ';' + buffer.back() = u'"'; + buffer += u'>'; + } + } + } + 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 (" << definition().name() << ") - Theme (" << theme().name() - << ")\"/>\n"; + *d->out << "<meta name=\"generator\" content=\"KF5::SyntaxHighlighting - Definition (" << definition.name() << ") - Theme (" << theme.name() << ")\"/>\n"; *d->out << "</head><body"; - *d->out << " style=\"background-color:" << toHtmlRgbaString(QColor::fromRgba(theme().editorColor(Theme::BackgroundColor))); - if (theme().textColor(Theme::Normal)) { - *d->out << ";color:" << toHtmlRgbaString(QColor::fromRgba(theme().textColor(Theme::Normal))); + *d->out << " style=\"background-color:" << toHtmlRgbaString(QColor::fromRgba(theme.editorColor(Theme::BackgroundColor))); + if (theme.textColor(Theme::Normal)) { + *d->out << ";color:" << toHtmlRgbaString(QColor::fromRgba(theme.textColor(Theme::Normal))); } *d->out << "\"><pre>\n"; QTextStream in(dev); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - in.setCodec("UTF-8"); -#endif while (in.readLineInto(&d->currentLine)) { state = highlightLine(d->currentLine, state); *d->out << "\n"; @@ -155,38 +198,24 @@ void HtmlHighlighter::applyFormat(int offset, int length, const Format &format) return; } - // collect potential output, cheaper than thinking about "is there any?" - QVarLengthArray<QString, 16> formatOutput; - if (format.hasTextColor(theme())) { - formatOutput << QStringLiteral("color:") << toHtmlRgbaString(format.textColor(theme())) << QStringLiteral(";"); - } - if (format.hasBackgroundColor(theme())) { - formatOutput << QStringLiteral("background-color:") << toHtmlRgbaString(format.backgroundColor(theme())) << 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;"); - } + Q_D(HtmlHighlighter); - if (!formatOutput.isEmpty()) { - *d->out << "<span style=\""; - for (const auto &out : std::as_const(formatOutput)) { - *d->out << out; - } - *d->out << "\">"; + auto const &htmlStyle = d->htmlStyles[format.id()]; + + if (!htmlStyle.isEmpty()) { + *d->out << htmlStyle; } - *d->out << d->currentLine.mid(offset, length).toHtmlEscaped(); + for (QChar ch : QStringView(d->currentLine).mid(offset, length)) { + if (ch == u'<') + *d->out << QStringLiteral("<"); + else if (ch == u'&') + *d->out << QStringLiteral("&"); + else + *d->out << ch; + } - if (!formatOutput.isEmpty()) { - *d->out << "</span>"; + if (!htmlStyle.isEmpty()) { + *d->out << QStringLiteral("</span>"); } } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h index 8754057345..741cb85103 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/htmlhighlighter.h @@ -10,10 +10,11 @@ #include "abstracthighlighter.h" #include "ksyntaxhighlighting_export.h" -#include <QIODevice> #include <QString> -#include <memory> +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE namespace KSyntaxHighlighting { @@ -35,7 +36,7 @@ protected: void applyFormat(int offset, int length, const Format &format) override; private: - std::unique_ptr<HtmlHighlighterPrivate> d; + Q_DECLARE_PRIVATE(HtmlHighlighter) }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp index 133b6d28ac..847f6af6d4 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/keywordlist.cpp @@ -47,7 +47,7 @@ bool KeywordList::contains(QStringView str, Qt::CaseSensitivity caseSensitive) c /** * search with right predicate */ - return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), QStringView(str), KeywordComparator{caseSensitive}); + return std::binary_search(vectorToSearch.begin(), vectorToSearch.end(), str, KeywordComparator{caseSensitive}); } void KeywordList::load(QXmlStreamReader &reader) @@ -103,10 +103,7 @@ void KeywordList::initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive /** * fill vector with refs to keywords */ - vectorToSort.reserve(m_keywords.size()); - for (const auto &keyword : std::as_const(m_keywords)) { - vectorToSort.push_back(keyword); - } + vectorToSort.assign(m_keywords.constBegin(), m_keywords.constEnd()); /** * sort with right predicate diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h index 1e0f7c6102..7112d4e291 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/matchresult_p.h @@ -41,9 +41,9 @@ public: * @param offset offset of match * @param captures captures of the match */ - explicit MatchResult(const int offset, const QStringList &captures) + explicit MatchResult(const int offset, QStringList &&captures) : m_offset(offset) - , m_captures(captures) + , m_captures(std::move(captures)) { } @@ -69,7 +69,7 @@ public: * Captures of the match. * @return captured text of this match */ - const QStringList &captures() const + QStringList &captures() { return m_captures; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp index a12d4ba1a3..07c28454c5 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.cpp @@ -77,9 +77,9 @@ Definition findHighestPriorityDefinitionIf(const QMap<QString, Definition> &defs } template<typename UnaryPredicate> -QVector<Definition> findDefinitionsIf(const QMap<QString, Definition> &defs, UnaryPredicate predicate) +QList<Definition> findDefinitionsIf(const QMap<QString, Definition> &defs, UnaryPredicate predicate) { - QVector<Definition> matches; + QList<Definition> matches; std::copy_if(defs.cbegin(), defs.cend(), std::back_inserter(matches), predicate); std::stable_sort(matches.begin(), matches.end(), [](const Definition &lhs, const Definition &rhs) { return lhs.priority() > rhs.priority(); @@ -127,7 +127,7 @@ Definition Repository::definitionForFileName(const QString &fileName) const return findHighestPriorityDefinitionIf(d->m_defs, anyWildcardMatches(fileNameFromFilePath(fileName))); } -QVector<Definition> Repository::definitionsForFileName(const QString &fileName) const +QList<Definition> Repository::definitionsForFileName(const QString &fileName) const { return findDefinitionsIf(d->m_defs, anyWildcardMatches(fileNameFromFilePath(fileName))); } @@ -137,22 +137,22 @@ Definition Repository::definitionForMimeType(const QString &mimeType) const return findHighestPriorityDefinitionIf(d->m_defs, anyMimeTypeEquals(mimeType)); } -QVector<Definition> Repository::definitionsForMimeType(const QString &mimeType) const +QList<Definition> Repository::definitionsForMimeType(const QString &mimeType) const { return findDefinitionsIf(d->m_defs, anyMimeTypeEquals(mimeType)); } -QVector<Definition> Repository::definitions() const +QList<Definition> Repository::definitions() const { return d->m_sortedDefs; } -QVector<Theme> Repository::themes() const +QList<Theme> Repository::themes() const { return d->m_themes; } -static auto lowerBoundTheme(const QVector<KSyntaxHighlighting::Theme> &themes, QStringView themeName) +static auto lowerBoundTheme(const QList<KSyntaxHighlighting::Theme> &themes, QStringView themeName) { return std::lower_bound(themes.begin(), themes.end(), themeName, [](const Theme &lhs, QStringView rhs) { return lhs.name() < rhs; @@ -177,42 +177,32 @@ Theme Repository::defaultTheme(Repository::DefaultTheme t) const return theme(QStringLiteral("Breeze Light")); } -Theme Repository::defaultTheme(Repository::DefaultTheme t) -{ - return std::as_const(*this).defaultTheme(t); -} - Theme Repository::themeForPalette(const QPalette &palette) const { const auto base = palette.color(QPalette::Base); + const auto highlight = palette.color(QPalette::Highlight).rgb(); - // find themes with matching background colors - QVector<const KSyntaxHighlighting::Theme *> matchingThemes; + // find themes with matching background and highlight colors + const Theme *firstMatchingTheme = nullptr; for (const auto &theme : std::as_const(d->m_themes)) { - const auto background = theme.editorColor(KSyntaxHighlighting::Theme::EditorColorRole::BackgroundColor); + const auto background = theme.editorColor(Theme::EditorColorRole::BackgroundColor); if (background == base.rgb()) { - matchingThemes.append(&theme); - } - } - if (!matchingThemes.empty()) { - // if there's multiple, search for one with a matching highlight color - const auto highlight = palette.color(QPalette::Highlight); - for (const auto *theme : std::as_const(matchingThemes)) { - auto selection = theme->editorColor(KSyntaxHighlighting::Theme::EditorColorRole::TextSelection); - if (selection == highlight.rgb()) { - return *theme; + // find theme with a matching highlight color + auto selection = theme.editorColor(Theme::EditorColorRole::TextSelection); + if (selection == highlight) { + return theme; + } + if (!firstMatchingTheme) { + firstMatchingTheme = &theme; } } - return *matchingThemes.first(); + } + if (firstMatchingTheme) { + return *firstMatchingTheme; } // fallback to just use the default light or dark theme - return defaultTheme((base.lightness() < 128) ? KSyntaxHighlighting::Repository::DarkTheme : KSyntaxHighlighting::Repository::LightTheme); -} - -Theme Repository::themeForPalette(const QPalette &palette) -{ - return std::as_const(*this).themeForPalette(palette); + return defaultTheme((base.lightness() < 128) ? Repository::DarkTheme : Repository::LightTheme); } void RepositoryPrivate::load(Repository *repo) @@ -238,12 +228,6 @@ void RepositoryPrivate::load(Repository *repo) QStandardPaths::LocateDirectory)) { loadSyntaxFolder(repo, dir); } - - // backward compatibility with Kate - for (const auto &dir : - QStandardPaths::locateAll(QStandardPaths::GenericDataLocation, QStringLiteral("katepart5/syntax"), QStandardPaths::LocateDirectory)) { - loadSyntaxFolder(repo, dir); - } #endif // default resources are always used, this is the one location that has a index cbor file @@ -377,25 +361,27 @@ void RepositoryPrivate::addTheme(const Theme &theme) } } -quint16 RepositoryPrivate::foldingRegionId(const QString &defName, const QString &foldName) +int 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(); } + Q_ASSERT(m_foldingRegionId < std::numeric_limits<int>::max()); m_foldingRegionIds.insert(qMakePair(defName, foldName), ++m_foldingRegionId); return m_foldingRegionId; } -quint16 RepositoryPrivate::nextFormatId() +int RepositoryPrivate::nextFormatId() { - Q_ASSERT(m_formatId < std::numeric_limits<quint16>::max()); + Q_ASSERT(m_formatId < std::numeric_limits<int>::max()); return ++m_formatId; } void Repository::reload() { - qCDebug(Log) << "Reloading syntax definitions!"; + Q_EMIT aboutToReload(); + for (const auto &def : std::as_const(d->m_sortedDefs)) { DefinitionData::get(def)->clear(); } @@ -410,6 +396,8 @@ void Repository::reload() d->m_formatId = 0; d->load(this); + + Q_EMIT reloaded(); } void Repository::addCustomSearchPath(const QString &path) @@ -418,7 +406,9 @@ void Repository::addCustomSearchPath(const QString &path) reload(); } -QVector<QString> Repository::customSearchPaths() const +QList<QString> Repository::customSearchPaths() const { return d->m_customSearchPaths; } + +#include "moc_repository.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h index 7c74753bfe..612ba54d6a 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository.h @@ -9,7 +9,8 @@ #include "ksyntaxhighlighting_export.h" -#include <QVector> +#include <QList> +#include <QObject> #include <QtGlobal> #include <memory> @@ -76,13 +77,6 @@ class Theme; * 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. @@ -124,8 +118,12 @@ class Theme; * @see Definition, Theme, AbstractHighlighter * @since 5.28 */ -class KSYNTAXHIGHLIGHTING_EXPORT Repository +class KSYNTAXHIGHLIGHTING_EXPORT Repository : public QObject { + Q_OBJECT + Q_PROPERTY(QList<KSyntaxHighlighting::Definition> definitions READ definitions NOTIFY reloaded) + Q_PROPERTY(QList<KSyntaxHighlighting::Theme> themes READ themes NOTIFY reloaded) + public: /** * Create a new syntax definition repository. @@ -148,7 +146,7 @@ public: * Therefore, only the string "JavaScript" will return a valid * Definition file. */ - Definition definitionForName(const QString &defName) const; + Q_INVOKABLE KSyntaxHighlighting::Definition definitionForName(const QString &defName) const; /** * Returns the best matching Definition for the file named @p fileName. @@ -159,7 +157,7 @@ public: * If no match is found, Definition::isValid() of the returned instance * returns false. */ - Definition definitionForFileName(const QString &fileName) const; + Q_INVOKABLE KSyntaxHighlighting::Definition definitionForFileName(const QString &fileName) const; /** * Returns all Definition%s for the file named @p fileName sorted by priority. @@ -168,7 +166,7 @@ public: * * @since 5.56 */ - QVector<Definition> definitionsForFileName(const QString &fileName) const; + Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitionsForFileName(const QString &fileName) const; /** * Returns the best matching Definition to the type named @p mimeType @@ -178,34 +176,34 @@ public: * * @since 5.50 */ - Definition definitionForMimeType(const QString &mimeType) const; + Q_INVOKABLE KSyntaxHighlighting::Definition definitionForMimeType(const QString &mimeType) const; /** * Returns all Definition%s to the type named @p mimeType sorted by priority * * @since 5.56 */ - QVector<Definition> definitionsForMimeType(const QString &mimeType) const; + Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitionsForMimeType(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; + Q_INVOKABLE QList<KSyntaxHighlighting::Definition> definitions() const; /** * Returns all available color themes. * The returned list should never be empty. */ - QVector<Theme> themes() const; + Q_INVOKABLE QList<KSyntaxHighlighting::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; + Q_INVOKABLE KSyntaxHighlighting::Theme theme(const QString &themeName) const; /** * Built-in default theme types. @@ -217,21 +215,14 @@ public: //! Theme with a dark background color. DarkTheme }; + Q_ENUM(DefaultTheme) /** * Returns a default theme instance of the given type. * The returned Theme is guaranteed to be a valid theme. * @since 5.79 */ - Theme defaultTheme(DefaultTheme t = LightTheme) const; - - /** - * Returns a default theme instance of the given type. - * The returned Theme is guaranteed to be a valid theme. - * - * KF6: remove in favor of const variant - */ - Theme defaultTheme(DefaultTheme t = LightTheme); + Q_INVOKABLE KSyntaxHighlighting::Theme defaultTheme(DefaultTheme t = LightTheme) const; /** * Returns the best matching theme for the given palette @@ -240,14 +231,6 @@ public: Theme themeForPalette(const QPalette &palette) const; /** - * Returns the best matching theme for the given palette - * @since 5.77 - * - * KF6: remove in favor of const variant - **/ - Theme themeForPalette(const QPalette &palette); - - /** * Reloads the repository. * This is a moderately expensive operations and should thus only be * triggered when the installed syntax definition files changed. @@ -278,7 +261,20 @@ public: * @see addCustomSearchPath() * @since 5.39 */ - QVector<QString> customSearchPaths() const; + QList<QString> customSearchPaths() const; + +Q_SIGNALS: + /** + * This signal is emitted before the reload is started. + * @since 6.0 + */ + void aboutToReload(); + + /** + * This signal is emitted when the reload is finished. + * @since 6.0 + */ + void reloaded(); private: Q_DISABLE_COPY(Repository) diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h index 447cfae699..bb9f8ba082 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/repository_p.h @@ -8,11 +8,11 @@ #define KSYNTAXHIGHLIGHTING_REPOSITORY_P_H #include <QHash> -#include <QVector> +#include <QList> +#include <QMap> +#include <QString> -QT_BEGIN_NAMESPACE -class QString; -QT_END_NAMESPACE +#include "dynamicregexpcache_p.h" namespace KSyntaxHighlighting { @@ -36,22 +36,24 @@ public: void loadThemeFolder(const QString &path); void addTheme(const Theme &theme); - quint16 foldingRegionId(const QString &defName, const QString &foldName); - quint16 nextFormatId(); + int foldingRegionId(const QString &defName, const QString &foldName); + int nextFormatId(); - QVector<QString> m_customSearchPaths; + QList<QString> m_customSearchPaths; // sorted map to have deterministic iteration order for e.g. definitionsForFileName QMap<QString, Definition> m_defs; // this vector is sorted by translated sections/names - QVector<Definition> m_sortedDefs; + QList<Definition> m_sortedDefs; - QVector<Theme> m_themes; + QList<Theme> m_themes; - QHash<QPair<QString, QString>, quint16> m_foldingRegionIds; - quint16 m_foldingRegionId = 0; - quint16 m_formatId = 0; + QHash<QPair<QString, QString>, int> m_foldingRegionIds; + int m_foldingRegionId = 0; + int m_formatId = 0; + + DynamicRegexpCache m_dynamicRegexpCache; }; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp index 1d02bd6ac3..186ed16120 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule.cpp @@ -1,20 +1,19 @@ /* SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org> - SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen@gmail.com> + SPDX-FileCopyrightText: 2020 Jonathan Poelen <jonathan.poelen+kde@gmail.com> SPDX-License-Identifier: MIT */ #include "context_p.h" #include "definition_p.h" +#include "dynamicregexpcache_p.h" #include "ksyntaxhighlighting_logging.h" #include "rule_p.h" #include "worddelimiters_p.h" #include "xml_p.h" -#include <QString> - using namespace KSyntaxHighlighting; // QChar::isDigit() match any digit in unicode (romain numeral, etc) @@ -90,8 +89,8 @@ static int matchEscapedChar(QStringView text, int 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)); + for (int i = captures.size(); i >= 1; --i) { + result.replace(QLatin1Char('%') + QString::number(i), quote ? QRegularExpression::escape(captures.at(i - 1)) : captures.at(i - 1)); } return result; } @@ -233,7 +232,7 @@ AnyChar::AnyChar(const HighlightingContextData::Rule::AnyChar &data) { } -MatchResult AnyChar::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult AnyChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (m_chars.contains(text.at(offset))) { return offset + 1; @@ -243,15 +242,15 @@ MatchResult AnyChar::doMatch(QStringView text, int offset, const QStringList &) DetectChar::DetectChar(const HighlightingContextData::Rule::DetectChar &data) : m_char(data.char1) - , m_captureIndex(data.dynamic ? data.char1.digitValue() : 0) + , m_captureIndex((data.dynamic ? data.char1.digitValue() : 0) - 1) { m_dynamic = data.dynamic; } -MatchResult DetectChar::doMatch(QStringView text, int offset, const QStringList &captures) const +MatchResult DetectChar::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &) const { if (m_dynamic) { - if (m_captureIndex == 0 || captures.size() <= m_captureIndex || captures.at(m_captureIndex).isEmpty()) { + if (m_captureIndex == -1 || captures.size() <= m_captureIndex || captures.at(m_captureIndex).isEmpty()) { return offset; } if (text.at(offset) == captures.at(m_captureIndex).at(0)) { @@ -272,7 +271,7 @@ Detect2Chars::Detect2Chars(const HighlightingContextData::Rule::Detect2Chars &da { } -MatchResult Detect2Chars::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult Detect2Chars::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (text.size() - offset < 2) { return offset; @@ -283,7 +282,7 @@ MatchResult Detect2Chars::doMatch(QStringView text, int offset, const QStringLis return offset; } -MatchResult DetectIdentifier::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult DetectIdentifier::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (!text.at(offset).isLetter() && text.at(offset) != QLatin1Char('_')) { return offset; @@ -299,7 +298,7 @@ MatchResult DetectIdentifier::doMatch(QStringView text, int offset, const QStrin return text.size(); } -MatchResult DetectSpaces::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult DetectSpaces::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { while (offset < text.size() && text.at(offset).isSpace()) { ++offset; @@ -313,7 +312,7 @@ Float::Float(DefinitionData &def, const HighlightingContextData::Rule::Float &da resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); } -MatchResult Float::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult Float::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) { return offset; @@ -358,7 +357,7 @@ MatchResult Float::doMatch(QStringView text, int offset, const QStringList &) co return expOffset; } -MatchResult HlCChar::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult HlCChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (text.size() < offset + 3) { return offset; @@ -393,7 +392,7 @@ HlCHex::HlCHex(DefinitionData &def, const HighlightingContextData::Rule::HlCHex resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); } -MatchResult HlCHex::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult HlCHex::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) { return offset; @@ -427,7 +426,7 @@ HlCOct::HlCOct(DefinitionData &def, const HighlightingContextData::Rule::HlCOct resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); } -MatchResult HlCOct::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult HlCOct::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) { return offset; @@ -453,7 +452,7 @@ MatchResult HlCOct::doMatch(QStringView text, int offset, const QStringList &) c return offset; } -MatchResult HlCStringChar::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult HlCStringChar::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { return matchEscapedChar(text, offset); } @@ -464,7 +463,7 @@ IncludeRules::IncludeRules(const HighlightingContextData::Rule::IncludeRules &da { } -MatchResult IncludeRules::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult IncludeRules::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { Q_UNUSED(text); qCWarning(Log) << "Unresolved include rule"; @@ -477,7 +476,7 @@ Int::Int(DefinitionData &def, const HighlightingContextData::Rule::Int &data) resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); } -MatchResult Int::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult Int::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (offset > 0 && !m_wordDelimiters.contains(text.at(offset - 1))) { return offset; @@ -521,9 +520,10 @@ KeywordListRule::KeywordListRule(const KeywordList &keywordList, DefinitionData , m_caseSensitivity(data.hasCaseSensitivityOverride ? data.caseSensitivityOverride : keywordList.caseSensitivity()) { resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); + m_hasSkipOffset = true; } -MatchResult KeywordListRule::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult KeywordListRule::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { auto newOffset = offset; while (text.size() > newOffset && !m_wordDelimiters.contains(text.at(newOffset))) { @@ -546,7 +546,7 @@ LineContinue::LineContinue(const HighlightingContextData::Rule::LineContinue &da { } -MatchResult LineContinue::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult LineContinue::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (offset == text.size() - 1 && text.at(offset) == m_char) { return offset + 1; @@ -560,7 +560,7 @@ RangeDetect::RangeDetect(const HighlightingContextData::Rule::RangeDetect &data) { } -MatchResult RangeDetect::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult RangeDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (text.size() - offset < 2) { return offset; @@ -591,25 +591,16 @@ static QRegularExpression::PatternOptions makePattenOptions(const HighlightingCo static void resolveRegex(QRegularExpression ®exp, Context *context) { - if (!regexp.isValid()) { - // DontCaptureOption with back reference capture is an error, remove this option then try again - regexp.setPatternOptions(regexp.patternOptions() & ~QRegularExpression::DontCaptureOption); + bool enableCapture = context && context->hasDynamicRule(); - if (!regexp.isValid()) { - qCDebug(Log) << "Invalid regexp:" << regexp.pattern(); - } - - return; + // disable DontCaptureOption when reference a context with dynamic rule or + // with invalid regex because DontCaptureOption with back reference capture is an error + if (enableCapture || !regexp.isValid()) { + regexp.setPatternOptions(regexp.patternOptions() & ~QRegularExpression::DontCaptureOption); } - // disable DontCaptureOption when reference a context with dynamic rule - if (context) { - for (const Rule::Ptr &rule : context->rules()) { - if (rule->isDynamic()) { - regexp.setPatternOptions(regexp.patternOptions() & ~QRegularExpression::DontCaptureOption); - break; - } - } + if (!regexp.isValid()) { + qCDebug(Log) << "Invalid regexp:" << regexp.pattern(); } } @@ -618,15 +609,25 @@ static MatchResult regexMatch(const QRegularExpression ®exp, QStringView text /** * match the pattern */ +#if QT_VERSION >= QT_VERSION_CHECK(6, 5, 0) + const auto result = regexp.matchView(text, offset, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption); +#else const auto result = regexp.match(text, offset, QRegularExpression::NormalMatch, QRegularExpression::DontCheckSubjectStringMatchOption); +#endif 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()); + int lastCapturedIndex = result.lastCapturedIndex(); + if (lastCapturedIndex > 0) { + QStringList captures; + captures.reserve(lastCapturedIndex); + // ignore the capturing group number 0 + for (int i = 1; i <= lastCapturedIndex; ++i) + captures.push_back(result.captured(i)); + return MatchResult(offset + result.capturedLength(), std::move(captures)); } /** @@ -645,20 +646,17 @@ static MatchResult regexMatch(const QRegularExpression ®exp, QStringView text RegExpr::RegExpr(const HighlightingContextData::Rule::RegExpr &data) : m_regexp(data.pattern, makePattenOptions(data)) { + m_hasSkipOffset = true; } void RegExpr::resolve() { - if (m_isResolved) { - return; - } - m_isResolved = true; resolveRegex(m_regexp, context().context()); } -MatchResult RegExpr::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult RegExpr::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (Q_UNLIKELY(!m_isResolved)) { const_cast<RegExpr *>(this)->resolve(); @@ -672,14 +670,11 @@ DynamicRegExpr::DynamicRegExpr(const HighlightingContextData::Rule::RegExpr &dat , m_patternOptions(makePattenOptions(data)) { m_dynamic = true; + m_hasSkipOffset = true; } void DynamicRegExpr::resolve() { - if (m_isResolved) { - return; - } - m_isResolved = true; QRegularExpression regexp(m_pattern, m_patternOptions); @@ -687,7 +682,7 @@ void DynamicRegExpr::resolve() m_patternOptions = regexp.patternOptions(); } -MatchResult DynamicRegExpr::doMatch(QStringView text, int offset, const QStringList &captures) const +MatchResult DynamicRegExpr::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &dynamicRegexpCache) const { if (Q_UNLIKELY(!m_isResolved)) { const_cast<DynamicRegExpr *>(this)->resolve(); @@ -696,8 +691,8 @@ MatchResult DynamicRegExpr::doMatch(QStringView text, int offset, const QStringL /** * create new pattern with right instantiation */ - const QRegularExpression regexp(replaceCaptures(m_pattern, captures, true), m_patternOptions); - + auto pattern = replaceCaptures(m_pattern, captures, true); + auto ®exp = dynamicRegexpCache.compileRegexp(std::move(pattern), m_patternOptions); return regexMatch(regexp, text, offset); } @@ -707,7 +702,7 @@ StringDetect::StringDetect(const HighlightingContextData::Rule::StringDetect &da { } -MatchResult StringDetect::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult StringDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { return matchString(m_string, text, offset, m_caseSensitivity); } @@ -719,7 +714,7 @@ DynamicStringDetect::DynamicStringDetect(const HighlightingContextData::Rule::St m_dynamic = true; } -MatchResult DynamicStringDetect::doMatch(QStringView text, int offset, const QStringList &captures) const +MatchResult DynamicStringDetect::doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &) const { /** * for dynamic case: create new pattern with right instantiation @@ -736,7 +731,7 @@ WordDetect::WordDetect(DefinitionData &def, const HighlightingContextData::Rule: resolveAdditionalWordDelimiters(m_wordDelimiters, data.wordDelimiters); } -MatchResult WordDetect::doMatch(QStringView text, int offset, const QStringList &) const +MatchResult WordDetect::doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const { if (text.size() - 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 index 7536d92e80..bc5f367ad6 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/rule_p.h @@ -27,6 +27,7 @@ namespace KSyntaxHighlighting class WordDelimiters; class DefinitionData; class IncludeRules; +class DynamicRegexpCache; class Rule { @@ -83,7 +84,15 @@ public: return m_type == Type::LineContinue; } - virtual MatchResult doMatch(QStringView text, int offset, const QStringList &captures) const = 0; + // If true, then the rule uses the skipOffset parameter of MatchResult. + // This is used by AbstractHighlighter::highlightLine() to look for a rule + // in the skipOffsets cache only if it can be found there. + bool hasSkipOffset() const + { + return m_hasSkipOffset; + } + + virtual MatchResult doMatch(QStringView text, int offset, const QStringList &captures, DynamicRegexpCache &dynamicRegexpCache) const = 0; static Rule::Ptr create(DefinitionData &def, const HighlightingContextData::Rule &ruleData, QStringView lookupContextName); @@ -98,6 +107,7 @@ private: IncludeRules, }; +private: Format m_attributeFormat; ContextSwitch m_context; int m_column = -1; @@ -108,6 +118,7 @@ private: bool m_lookAhead = false; protected: + bool m_hasSkipOffset = false; bool m_dynamic = false; }; @@ -117,10 +128,10 @@ public: AnyChar(const HighlightingContextData::Rule::AnyChar &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: - QString m_chars; + WordDelimiters m_chars; }; class DetectChar final : public Rule @@ -129,7 +140,7 @@ public: DetectChar(const HighlightingContextData::Rule::DetectChar &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QChar m_char; @@ -142,7 +153,7 @@ public: Detect2Chars(const HighlightingContextData::Rule::Detect2Chars &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QChar m_char1; @@ -152,13 +163,13 @@ private: class DetectIdentifier final : public Rule { protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; }; class DetectSpaces final : public Rule { protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; }; class Float final : public Rule @@ -167,7 +178,7 @@ public: Float(DefinitionData &def, const HighlightingContextData::Rule::Float &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; @@ -189,7 +200,7 @@ public: } protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QString m_contextName; @@ -202,7 +213,7 @@ public: Int(DefinitionData &def, const HighlightingContextData::Rule::Int &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; @@ -211,7 +222,7 @@ private: class HlCChar final : public Rule { protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; }; class HlCHex final : public Rule @@ -220,7 +231,7 @@ public: HlCHex(DefinitionData &def, const HighlightingContextData::Rule::HlCHex &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; @@ -232,7 +243,7 @@ public: HlCOct(DefinitionData &def, const HighlightingContextData::Rule::HlCOct &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; @@ -241,7 +252,7 @@ private: class HlCStringChar final : public Rule { protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; }; class KeywordListRule final : public Rule @@ -252,7 +263,7 @@ public: static Rule::Ptr create(DefinitionData &def, const HighlightingContextData::Rule::Keyword &data, QStringView lookupContextName); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; @@ -266,7 +277,7 @@ public: LineContinue(const HighlightingContextData::Rule::LineContinue &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QChar m_char; @@ -278,7 +289,7 @@ public: RangeDetect(const HighlightingContextData::Rule::RangeDetect &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QChar m_begin; @@ -291,7 +302,7 @@ public: RegExpr(const HighlightingContextData::Rule::RegExpr &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: void resolve(); @@ -305,7 +316,7 @@ public: DynamicRegExpr(const HighlightingContextData::Rule::RegExpr &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: void resolve(); @@ -320,7 +331,7 @@ public: StringDetect(const HighlightingContextData::Rule::StringDetect &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QString m_string; @@ -333,7 +344,7 @@ public: DynamicStringDetect(const HighlightingContextData::Rule::StringDetect &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: QString m_string; @@ -346,7 +357,7 @@ public: WordDetect(DefinitionData &def, const HighlightingContextData::Rule::WordDetect &data); protected: - MatchResult doMatch(QStringView text, int offset, const QStringList &) const override; + MatchResult doMatch(QStringView text, int offset, const QStringList &, DynamicRegexpCache &) const override; private: WordDelimiters m_wordDelimiters; diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp index fc44a6dbd4..dca58b35b7 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.cpp @@ -1,4 +1,4 @@ -/* +/* SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> SPDX-FileCopyrightText: 2018 Christoph Cullmann <cullmann@kde.org> @@ -14,36 +14,23 @@ using namespace KSyntaxHighlighting; -StateData *StateData::get(State &state) +StateData *StateData::reset(State &state) { - // create state data on demand, to make default state construction cheap - if (!state.d) { - state.d = new StateData(); - } else { - state.d.detach(); - } - return state.d.data(); -} - -bool StateData::isEmpty() const -{ - return m_contextStack.isEmpty(); -} - -void StateData::clear() -{ - m_contextStack.clear(); + auto *p = new StateData(); + state.d.reset(p); + return p; } -int StateData::size() const +StateData *StateData::detach(State &state) { - return m_contextStack.size(); + state.d.detach(); + return state.d.data(); } -void StateData::push(Context *context, const QStringList &captures) +void StateData::push(Context *context, QStringList &&captures) { Q_ASSERT(context); - m_contextStack.push_back(qMakePair(context, captures)); + m_contextStack.push_back(StackValue{context, std::move(captures)}); } bool StateData::pop(int popCount) @@ -54,42 +41,23 @@ bool StateData::pop(int popCount) } // keep the initial context alive in any case - Q_ASSERT(!isEmpty()); - const bool initialContextSurvived = m_contextStack.size() > popCount; + Q_ASSERT(!m_contextStack.empty()); + const bool initialContextSurvived = int(m_contextStack.size()) > popCount; m_contextStack.resize(std::max(1, int(m_contextStack.size()) - popCount)); return initialContextSurvived; } -Context *StateData::topContext() const -{ - Q_ASSERT(!isEmpty()); - return m_contextStack.last().first; -} +State::State() = default; -const QStringList &StateData::topCaptures() const -{ - Q_ASSERT(!isEmpty()); - return m_contextStack.last().second; -} +State::State(State &&other) noexcept = default; -State::State() -{ -} +State::State(const State &other) noexcept = default; -State::State(const State &other) - : d(other.d) -{ -} +State::~State() = default; -State::~State() -{ -} +State &State::operator=(State &&other) noexcept = default; -State &State::operator=(const State &other) -{ - d = other.d; - return *this; -} +State &State::operator=(const State &other) noexcept = default; bool State::operator==(const State &other) const { @@ -104,8 +72,13 @@ bool State::operator!=(const State &other) const bool State::indentationBasedFoldingEnabled() const { - if (!d || d->m_contextStack.isEmpty()) { + if (!d || d->m_contextStack.empty()) { return false; } - return d->m_contextStack.last().first->indentationBasedFoldingEnabled(); + return d->m_contextStack.back().context->indentationBasedFoldingEnabled(); +} + +std::size_t KSyntaxHighlighting::qHash(const State &state, std::size_t seed) +{ + return state.d ? qHashMulti(seed, *state.d) : 0; } diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/state.h b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h index 726ff32a88..3003a9b7cb 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/state.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state.h @@ -10,11 +10,15 @@ #include "ksyntaxhighlighting_export.h" #include <QExplicitlySharedDataPointer> +#include <QHash> namespace KSyntaxHighlighting { +class State; class StateData; +KSYNTAXHIGHLIGHTING_EXPORT std::size_t qHash(const State &state, std::size_t seed = 0); + /** 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 @@ -29,9 +33,11 @@ public: * in a document. */ State(); - State(const State &other); + State(State &&other) noexcept; + State(const State &other) noexcept; ~State(); - State &operator=(const State &rhs); + State &operator=(State &&rhs) noexcept; + State &operator=(const State &rhs) noexcept; /** Compares two states for equality. * For two equal states and identical text input, AbstractHighlighter @@ -56,13 +62,13 @@ public: private: friend class StateData; + KSYNTAXHIGHLIGHTING_EXPORT friend std::size_t qHash(const State &, std::size_t); QExplicitlySharedDataPointer<StateData> d; }; - } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::State, Q_RELOCATABLE_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 index 0248330304..4aee141681 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/state_p.h @@ -8,8 +8,10 @@ #ifndef KSYNTAXHIGHLIGHTING_STATE_P_H #define KSYNTAXHIGHLIGHTING_STATE_P_H +#include <vector> + #include <QSharedData> -#include <QVector> +#include <QStringList> #include "definitionref_p.h" @@ -21,15 +23,25 @@ class StateData : public QSharedData { friend class State; friend class AbstractHighlighter; + friend std::size_t qHash(const StateData &, std::size_t); public: StateData() = default; - static StateData *get(State &state); - bool isEmpty() const; - void clear(); - int size() const; - void push(Context *context, const QStringList &captures); + static StateData *reset(State &state); + static StateData *detach(State &state); + + static StateData *get(const State &state) + { + return state.d.data(); + } + + int size() const + { + return m_contextStack.size(); + } + + void push(Context *context, QStringList &&captures); /** * Pop the number of elements given from the top of the current stack. @@ -39,8 +51,25 @@ public: */ bool pop(int popCount); - Context *topContext() const; - const QStringList &topCaptures() const; + Context *topContext() const + { + return m_contextStack.back().context; + } + + const QStringList &topCaptures() const + { + return m_contextStack.back().captures; + } + + struct StackValue { + Context *context; + QStringList captures; + + bool operator==(const StackValue &other) const + { + return context == other.context && captures == other.captures; + } + }; private: /** @@ -51,9 +80,18 @@ private: /** * the context stack combines the active context + valid captures */ - QVector<QPair<Context *, QStringList>> m_contextStack; + std::vector<StackValue> m_contextStack; }; +inline std::size_t qHash(const StateData::StackValue &stackValue, std::size_t seed = 0) +{ + return qHashMulti(seed, stackValue.context, stackValue.captures); +} + +inline std::size_t qHash(const StateData &k, std::size_t seed = 0) +{ + return qHashMulti(seed, k.m_defId, qHashRange(k.m_contextStack.begin(), k.m_contextStack.end(), seed)); +} } #endif diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp index 4754da22c6..70b26a79bf 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.cpp @@ -7,10 +7,13 @@ #include "syntaxhighlighter.h" #include "abstracthighlighter_p.h" #include "definition.h" +#include "definition_p.h" #include "foldingregion.h" #include "format.h" +#include "format_p.h" #include "state.h" #include "theme.h" +#include "themedata_p.h" Q_DECLARE_METATYPE(QTextBlock) @@ -22,14 +25,26 @@ class TextBlockUserData : public QTextBlockUserData { public: State state; - QVector<FoldingRegion> foldingRegions; + QList<FoldingRegion> foldingRegions; }; class SyntaxHighlighterPrivate : public AbstractHighlighterPrivate { public: static FoldingRegion foldingRegion(const QTextBlock &startBlock); - QVector<FoldingRegion> foldingRegions; + void initTextFormat(QTextCharFormat &tf, const Format &format); + void computeTextFormats(); + + struct TextFormat { + QTextCharFormat tf; + /** + * id to check that the format belongs to the definition + */ + std::intptr_t ptrId; + }; + + QList<FoldingRegion> foldingRegions; + std::vector<TextFormat> tfs; }; } @@ -48,6 +63,52 @@ FoldingRegion SyntaxHighlighterPrivate::foldingRegion(const QTextBlock &startBlo return FoldingRegion(); } +void SyntaxHighlighterPrivate::initTextFormat(QTextCharFormat &tf, const Format &format) +{ + // always set the foreground color to avoid palette issues + tf.setForeground(format.textColor(m_theme)); + + if (format.hasBackgroundColor(m_theme)) { + tf.setBackground(format.backgroundColor(m_theme)); + } + if (format.isBold(m_theme)) { + tf.setFontWeight(QFont::Bold); + } + if (format.isItalic(m_theme)) { + tf.setFontItalic(true); + } + if (format.isUnderline(m_theme)) { + tf.setFontUnderline(true); + } + if (format.isStrikeThrough(m_theme)) { + tf.setFontStrikeOut(true); + } +} + +void SyntaxHighlighterPrivate::computeTextFormats() +{ + auto definitions = m_definition.includedDefinitions(); + definitions.append(m_definition); + + int maxId = 0; + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { + maxId = qMax(maxId, format.id()); + } + } + tfs.clear(); + tfs.resize(maxId + 1); + + // initialize tfs + for (const auto &definition : std::as_const(definitions)) { + for (const auto &format : std::as_const(DefinitionData::get(definition)->formats)) { + auto &tf = tfs[format.id()]; + tf.ptrId = FormatPrivate::ptrId(format); + initTextFormat(tf.tf, format); + } + } +} + SyntaxHighlighter::SyntaxHighlighter(QObject *parent) : QSyntaxHighlighter(parent) , AbstractHighlighter(new SyntaxHighlighterPrivate) @@ -68,13 +129,27 @@ SyntaxHighlighter::~SyntaxHighlighter() void SyntaxHighlighter::setDefinition(const Definition &def) { - const auto needsRehighlight = definition() != def; - AbstractHighlighter::setDefinition(def); + Q_D(SyntaxHighlighter); + + const auto needsRehighlight = d->m_definition != def; + if (DefinitionData::get(d->m_definition) != DefinitionData::get(def)) { + d->m_definition = def; + d->tfs.clear(); + } if (needsRehighlight) { rehighlight(); } } +void SyntaxHighlighter::setTheme(const Theme &theme) +{ + Q_D(SyntaxHighlighter); + if (ThemeData::get(d->m_theme) != ThemeData::get(theme)) { + d->m_theme = theme; + d->tfs.clear(); + } +} + bool SyntaxHighlighter::startsFoldingRegion(const QTextBlock &startBlock) const { return SyntaxHighlighterPrivate::foldingRegion(startBlock).type() == FoldingRegion::Begin; @@ -92,13 +167,13 @@ QTextBlock SyntaxHighlighter::findFoldingRegionEnd(const QTextBlock &startBlock) if (!data) { continue; } - for (auto it = data->foldingRegions.constBegin(); it != data->foldingRegions.constEnd(); ++it) { - if ((*it).id() != region.id()) { + for (const auto &foldingRegion : std::as_const(data->foldingRegions)) { + if (foldingRegion.id() != region.id()) { continue; } - if ((*it).type() == FoldingRegion::End) { + if (foldingRegion.type() == FoldingRegion::End) { --depth; - } else if ((*it).type() == FoldingRegion::Begin) { + } else if (foldingRegion.type() == FoldingRegion::Begin) { ++depth; } if (depth == 0) { @@ -114,30 +189,31 @@ void SyntaxHighlighter::highlightBlock(const QString &text) { Q_D(SyntaxHighlighter); - State state; + static const State emptyState; + const State *previousState = &emptyState; if (currentBlock().position() > 0) { const auto prevBlock = currentBlock().previous(); const auto prevData = dynamic_cast<TextBlockUserData *>(prevBlock.userData()); if (prevData) { - state = prevData->state; + previousState = &prevData->state; } } d->foldingRegions.clear(); - state = highlightLine(text, state); + auto newState = highlightLine(text, *previousState); auto data = dynamic_cast<TextBlockUserData *>(currentBlockUserData()); if (!data) { // first time we highlight this data = new TextBlockUserData; - data->state = state; + data->state = std::move(newState); 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 + if (data->state == newState && data->foldingRegions == d->foldingRegions) { // we ended up in the same state, so we are done here return; } - data->state = state; + data->state = std::move(newState); data->foldingRegions = d->foldingRegions; const auto nextBlock = currentBlock().next(); @@ -146,40 +222,35 @@ void SyntaxHighlighter::highlightBlock(const QString &text) } } -void SyntaxHighlighter::applyFormat(int offset, int length, const KSyntaxHighlighting::Format &format) +void SyntaxHighlighter::applyFormat(int offset, int length, const Format &format) { if (length == 0) { return; } - QTextCharFormat tf; - // always set the foreground color to avoid palette issues - tf.setForeground(format.textColor(theme())); + Q_D(SyntaxHighlighter); - 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); + if (Q_UNLIKELY(d->tfs.empty())) { + d->computeTextFormats(); } - QSyntaxHighlighter::setFormat(offset, length, tf); + const auto id = static_cast<std::size_t>(format.id()); + // This doesn't happen when format comes from the definition. + // But as the user can override the function to pass any format, this is a possible scenario. + if (id < d->tfs.size() && d->tfs[id].ptrId == FormatPrivate::ptrId(format)) { + QSyntaxHighlighter::setFormat(offset, length, d->tfs[id].tf); + } else { + QTextCharFormat tf; + d->initTextFormat(tf, format); + QSyntaxHighlighter::setFormat(offset, length, tf); + } } void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion region) { Q_UNUSED(offset); Q_UNUSED(length); - [[maybe_unused]] Q_D(SyntaxHighlighter); + Q_D(SyntaxHighlighter); if (region.type() == FoldingRegion::Begin) { d->foldingRegions.push_back(region); @@ -196,3 +267,5 @@ void SyntaxHighlighter::applyFolding(int offset, int length, FoldingRegion regio d->foldingRegions.push_back(region); } } + +#include "moc_syntaxhighlighter.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h index a57455d9ba..c19cb798dd 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/syntaxhighlighter.h @@ -32,6 +32,7 @@ public: ~SyntaxHighlighter() override; void setDefinition(const Definition &def) override; + void setTheme(const Theme &theme) override; /** Returns whether there is a folding region beginning at @p startBlock. * This only considers syntax-based folding regions, diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp index b23852f337..c54bb38b18 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.cpp @@ -103,3 +103,5 @@ QRgb Theme::editorColor(EditorColorRole role) const { return m_data->editorColor(role); } + +#include "moc_theme.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h index 37f9de1694..c3fb0e6b6e 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/theme.h @@ -67,11 +67,6 @@ class KSYNTAXHIGHLIGHTING_EXPORT Theme Q_PROPERTY(QString name READ name) Q_PROPERTY(QString translatedName READ translatedName) 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 @@ -342,7 +337,7 @@ private: /** * Constructor taking a shared ThemeData instance. */ - explicit Theme(ThemeData *data); + KSYNTAXHIGHLIGHTING_NO_EXPORT explicit Theme(ThemeData *data); friend class RepositoryPrivate; friend class ThemeData; @@ -356,7 +351,7 @@ private: } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::Theme, Q_RELOCATABLE_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 index 4f77dcc494..9d42d03db0 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata.cpp @@ -18,11 +18,6 @@ using namespace KSyntaxHighlighting; -ThemeData *ThemeData::get(const Theme &theme) -{ - return theme.m_data.data(); -} - ThemeData::ThemeData() { memset(m_editorColors, 0, sizeof(m_editorColors)); @@ -87,9 +82,18 @@ bool ThemeData::load(const QString &filePath) return false; } const QByteArray jsonData = loadFile.readAll(); + // look for metadata object + int metaDataStart = jsonData.indexOf("\"metadata\""); + int start = jsonData.indexOf('{', metaDataStart); + int end = jsonData.indexOf("}", metaDataStart); + if (start < 0 || end < 0) { + qCWarning(Log) << "Failed to parse theme file" << filePath << ":" + << "no metadata object found"; + return false; + } QJsonParseError parseError; - QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData, &parseError); + QJsonDocument jsonDoc = QJsonDocument::fromJson(jsonData.mid(start, (end + 1) - start), &parseError); if (parseError.error != QJsonParseError::NoError) { qCWarning(Log) << "Failed to parse theme file" << filePath << ":" << parseError.errorString(); return false; @@ -97,13 +101,34 @@ bool ThemeData::load(const QString &filePath) m_filePath = filePath; - QJsonObject obj = jsonDoc.object(); - // read metadata - const QJsonObject metadata = obj.value(QLatin1String("metadata")).toObject(); + QJsonObject metadata = jsonDoc.object(); m_name = metadata.value(QLatin1String("name")).toString(); m_revision = metadata.value(QLatin1String("revision")).toInt(); + return true; +} +void ThemeData::loadComplete() +{ + if (m_completelyLoaded) { + return; + } + m_completelyLoaded = true; + + QFile loadFile(m_filePath); + if (!loadFile.open(QIODevice::ReadOnly)) { + return; + } + 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" << m_filePath << ":" << parseError.errorString(); + return; + } + + QJsonObject obj = jsonDoc.object(); // read text styles const auto metaEnumStyle = QMetaEnum::fromType<Theme::TextStyle>(); const QJsonObject textStyles = obj.value(QLatin1String("text-styles")).toObject(); @@ -162,7 +187,7 @@ bool ThemeData::load(const QString &filePath) } } - return true; + return; } QString ThemeData::name() const @@ -187,6 +212,9 @@ QString ThemeData::filePath() const TextStyleData ThemeData::textStyle(Theme::TextStyle style) const { + if (!m_completelyLoaded) { + const_cast<ThemeData *>(this)->loadComplete(); + } return m_textStyles[style]; } @@ -232,12 +260,18 @@ bool ThemeData::isStrikeThrough(Theme::TextStyle style) const QRgb ThemeData::editorColor(Theme::EditorColorRole role) const { + if (!m_completelyLoaded) { + const_cast<ThemeData *>(this)->loadComplete(); + } 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 { + if (!m_completelyLoaded) { + const_cast<ThemeData *>(this)->loadComplete(); + } auto it = m_textStyleOverrides.find(definitionName); if (it != m_textStyleOverrides.end()) { return it->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 index 4ce87f0aaf..6ee772f172 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/themedata_p.h @@ -24,7 +24,10 @@ namespace KSyntaxHighlighting class ThemeData : public QSharedData { public: - static ThemeData *get(const Theme &theme); + static ThemeData *get(const Theme &theme) + { + return theme.m_data.data(); + } /** * Default constructor, creating an uninitialized ThemeData instance. @@ -37,6 +40,8 @@ public: */ bool load(const QString &filePath); + void loadComplete(); + /** * Returns the unique name of this Theme. */ @@ -140,6 +145,8 @@ private: //! on disk (in a read-only or a writeable location). QString m_filePath; + bool m_completelyLoaded = false; + //! TextStyles std::vector<TextStyleData> m_textStyles; @@ -154,7 +161,7 @@ private: } QT_BEGIN_NAMESPACE -Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_MOVABLE_TYPE); +Q_DECLARE_TYPEINFO(KSyntaxHighlighting::TextStyleData, Q_RELOCATABLE_TYPE); QT_END_NAMESPACE #endif // KSYNTAXHIGHLIGHTING_THEMEDATA_P_H diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp index c5401a57cc..ce55cd4b29 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters.cpp @@ -16,6 +16,12 @@ WordDelimiters::WordDelimiters() } } +WordDelimiters::WordDelimiters(QStringView str) + : asciiDelimiters{} +{ + append(str); +} + bool WordDelimiters::contains(QChar c) const { if (c.unicode() < 128) { diff --git a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h index ccad679a4e..c23670d634 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h +++ b/src/libs/3rdparty/syntax-highlighting/src/lib/worddelimiters_p.h @@ -27,6 +27,11 @@ public: WordDelimiters(); /** + * Initialize with a default delimiters. + */ + explicit WordDelimiters(QStringView str); + + /** * Returns @c true if @p c is a word delimiter; otherwise returns @c false. */ bool contains(QChar c) const; diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt b/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt index 9277c2aee7..1fb92ad220 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt +++ b/src/libs/3rdparty/syntax-highlighting/src/quick/CMakeLists.txt @@ -7,11 +7,10 @@ ecm_add_qml_module(kquicksyntaxhighlightingplugin URI "org.kde.syntaxhighlightin target_sources(kquicksyntaxhighlightingplugin PRIVATE kquicksyntaxhighlightingplugin.cpp kquicksyntaxhighlighter.cpp - repositorywrapper.cpp ) target_link_libraries(kquicksyntaxhighlightingplugin PRIVATE - KF5SyntaxHighlighting - Qt${QT_MAJOR_VERSION}::Quick + KF6SyntaxHighlighting + Qt6::Quick ) ecm_finalize_qml_module(kquicksyntaxhighlightingplugin DESTINATION ${KDE_INSTALL_QMLDIR}) diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp index eb795b1468..19cfbacf58 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.cpp @@ -49,7 +49,7 @@ QVariant KQuickSyntaxHighlighter::definition() const void KQuickSyntaxHighlighter::setDefinition(const QVariant &definition) { Definition def; - if (definition.type() == QVariant::String) { + if (definition.userType() == QMetaType::QString) { def = unwrappedRepository()->definitionForName(definition.toString()); } else { def = definition.value<Definition>(); @@ -73,9 +73,9 @@ QVariant KQuickSyntaxHighlighter::theme() const void KQuickSyntaxHighlighter::setTheme(const QVariant &theme) { Theme t; - if (theme.type() == QVariant::String) { + if (theme.userType() == QMetaType::QString) { t = unwrappedRepository()->theme(theme.toString()); - } else if (theme.type() == QVariant::Int) { + } else if (theme.userType() == QMetaType::Int) { t = unwrappedRepository()->defaultTheme(static_cast<Repository::DefaultTheme>(theme.toInt())); } else { t = theme.value<Theme>(); @@ -89,12 +89,12 @@ void KQuickSyntaxHighlighter::setTheme(const QVariant &theme) } } -RepositoryWrapper *KQuickSyntaxHighlighter::repository() const +Repository *KQuickSyntaxHighlighter::repository() const { return m_repository; } -void KQuickSyntaxHighlighter::setRepository(RepositoryWrapper *repository) +void KQuickSyntaxHighlighter::setRepository(Repository *repository) { if (m_repository == repository) { return; @@ -106,7 +106,9 @@ void KQuickSyntaxHighlighter::setRepository(RepositoryWrapper *repository) Repository *KQuickSyntaxHighlighter::unwrappedRepository() const { if (m_repository) { - return m_repository->m_repository; + return m_repository; } return defaultRepository(); } + +#include "moc_kquicksyntaxhighlighter.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h index 211f80d37f..b45c26339f 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h +++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlighter.h @@ -8,8 +8,6 @@ #ifndef KQUICKSYNTAXHIGHLIGHTER_H #define KQUICKSYNTAXHIGHLIGHTER_H -#include "repositorywrapper.h" - #include <KSyntaxHighlighting/Definition> #include <KSyntaxHighlighting/Theme> @@ -29,7 +27,7 @@ class KQuickSyntaxHighlighter : public QObject Q_PROPERTY(QObject *textEdit READ textEdit WRITE setTextEdit NOTIFY textEditChanged) Q_PROPERTY(QVariant definition READ definition WRITE setDefinition NOTIFY definitionChanged) Q_PROPERTY(QVariant theme READ theme WRITE setTheme NOTIFY themeChanged) - Q_PROPERTY(RepositoryWrapper *repository READ repository WRITE setRepository NOTIFY repositoryChanged) + Q_PROPERTY(KSyntaxHighlighting::Repository *repository READ repository WRITE setRepository NOTIFY repositoryChanged) public: explicit KQuickSyntaxHighlighter(QObject *parent = nullptr); @@ -44,8 +42,8 @@ public: QVariant theme() const; void setTheme(const QVariant &theme); - RepositoryWrapper *repository() const; - void setRepository(RepositoryWrapper *repository); + KSyntaxHighlighting::Repository *repository() const; + void setRepository(KSyntaxHighlighting::Repository *repository); Q_SIGNALS: void textEditChanged() const; @@ -59,7 +57,7 @@ private: QObject *m_textEdit; KSyntaxHighlighting::Definition m_definition; KSyntaxHighlighting::Theme m_theme; - RepositoryWrapper *m_repository = nullptr; + KSyntaxHighlighting::Repository *m_repository = nullptr; KSyntaxHighlighting::SyntaxHighlighter *m_highlighter = nullptr; }; diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp index 9aeb503ec5..5eb06862df 100644 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp +++ b/src/libs/3rdparty/syntax-highlighting/src/quick/kquicksyntaxhighlightingplugin.cpp @@ -7,7 +7,6 @@ #include "kquicksyntaxhighlightingplugin.h" #include "kquicksyntaxhighlighter.h" -#include "repositorywrapper.h" #include <KSyntaxHighlighting/Definition> #include <KSyntaxHighlighting/Repository> @@ -30,17 +29,18 @@ void KQuickSyntaxHighlightingPlugin::registerTypes(const char *uri) { Q_ASSERT(QLatin1String(uri) == QLatin1String("org.kde.syntaxhighlighting")); qRegisterMetaType<Definition>(); - qRegisterMetaType<QVector<Definition>>(); + qRegisterMetaType<QList<Definition>>(); qRegisterMetaType<Theme>(); - qRegisterMetaType<QVector<Theme>>(); + qRegisterMetaType<QList<Theme>>(); qmlRegisterType<KQuickSyntaxHighlighter>(uri, 1, 0, "SyntaxHighlighter"); - qmlRegisterUncreatableType<Definition>(uri, 1, 0, "Definition", {}); - qmlRegisterUncreatableType<Theme>(uri, 1, 0, "Theme", {}); - qmlRegisterSingletonType<RepositoryWrapper>(uri, 1, 0, "Repository", [](auto engine, auto scriptEngine) { + qmlRegisterUncreatableMetaObject(Definition::staticMetaObject, uri, 1, 0, "Definition", {}); + qmlRegisterUncreatableMetaObject(Theme::staticMetaObject, uri, 1, 0, "Theme", {}); + qmlRegisterSingletonType<Repository>(uri, 1, 0, "Repository", [](auto engine, auto scriptEngine) { (void)engine; - (void)scriptEngine; - auto repo = new RepositoryWrapper; - repo->m_repository = defaultRepository(); - return repo; + auto repo = defaultRepository(); + scriptEngine->setObjectOwnership(repo, QJSEngine::CppOwnership); + return defaultRepository(); }); } + +#include "moc_kquicksyntaxhighlightingplugin.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.cpp b/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.cpp deleted file mode 100644 index 733c799ed1..0000000000 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.cpp +++ /dev/null @@ -1,65 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> - - SPDX-License-Identifier: MIT -*/ - -#include "repositorywrapper.h" - -#include <KSyntaxHighlighting/Definition> -#include <KSyntaxHighlighting/Repository> -#include <KSyntaxHighlighting/Theme> - -using namespace KSyntaxHighlighting; - -RepositoryWrapper::RepositoryWrapper(QObject *parent) - : QObject(parent) -{ -} - -Definition RepositoryWrapper::definitionForName(const QString &defName) const -{ - return m_repository->definitionForName(defName); -} - -Definition RepositoryWrapper::definitionForFileName(const QString &fileName) const -{ - return m_repository->definitionForFileName(fileName); -} - -QVector<Definition> RepositoryWrapper::definitionsForFileName(const QString &fileName) const -{ - return m_repository->definitionsForFileName(fileName); -} - -Definition RepositoryWrapper::definitionForMimeType(const QString &mimeType) const -{ - return m_repository->definitionForMimeType(mimeType); -} - -QVector<Definition> RepositoryWrapper::definitionsForMimeType(const QString &mimeType) const -{ - return m_repository->definitionsForMimeType(mimeType); -} - -QVector<Definition> RepositoryWrapper::definitions() const -{ - return m_repository->definitions(); -} - -QVector<Theme> RepositoryWrapper::themes() const -{ - return m_repository->themes(); -} - -Theme RepositoryWrapper::theme(const QString &themeName) const -{ - return m_repository->theme(themeName); -} - -Theme RepositoryWrapper::defaultTheme(DefaultTheme t) const -{ - return m_repository->defaultTheme(static_cast<Repository::DefaultTheme>(t)); -} - -#include "moc_repositorywrapper.cpp" diff --git a/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.h b/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.h deleted file mode 100644 index d4fb8d251c..0000000000 --- a/src/libs/3rdparty/syntax-highlighting/src/quick/repositorywrapper.h +++ /dev/null @@ -1,45 +0,0 @@ -/* - SPDX-FileCopyrightText: 2021 Volker Krause <vkrause@kde.org> - - SPDX-License-Identifier: MIT -*/ - -#ifndef REPOSITORYWRAPPER_H -#define REPOSITORYWRAPPER_H - -#include <QObject> - -namespace KSyntaxHighlighting -{ -class Definition; -class Repository; -class Theme; -} - -// TODO KF6: merge this into KSyntaxHighlighting::Repository -class RepositoryWrapper : public QObject -{ - Q_OBJECT - // TODO KF6: NOTIFY on reload - Q_PROPERTY(QVector<KSyntaxHighlighting::Definition> definitions READ definitions CONSTANT) - Q_PROPERTY(QVector<KSyntaxHighlighting::Theme> themes READ themes CONSTANT) -public: - explicit RepositoryWrapper(QObject *parent = nullptr); - - Q_INVOKABLE KSyntaxHighlighting::Definition definitionForName(const QString &defName) const; - Q_INVOKABLE KSyntaxHighlighting::Definition definitionForFileName(const QString &fileName) const; - Q_INVOKABLE QVector<KSyntaxHighlighting::Definition> definitionsForFileName(const QString &fileName) const; - Q_INVOKABLE KSyntaxHighlighting::Definition definitionForMimeType(const QString &mimeType) const; - Q_INVOKABLE QVector<KSyntaxHighlighting::Definition> definitionsForMimeType(const QString &mimeType) const; - QVector<KSyntaxHighlighting::Definition> definitions() const; - - QVector<KSyntaxHighlighting::Theme> themes() const; - Q_INVOKABLE KSyntaxHighlighting::Theme theme(const QString &themeName) const; - enum DefaultTheme { LightTheme, DarkTheme }; - Q_ENUM(DefaultTheme) - Q_INVOKABLE KSyntaxHighlighting::Theme defaultTheme(DefaultTheme t = LightTheme) const; - - KSyntaxHighlighting::Repository *m_repository = nullptr; -}; - -#endif // REPOSITORYWRAPPER_H |