diff --git a/autotests/syntaxrepository_test.cpp b/autotests/syntaxrepository_test.cpp --- a/autotests/syntaxrepository_test.cpp +++ b/autotests/syntaxrepository_test.cpp @@ -398,6 +398,96 @@ QVERIFY(encodings.contains({ QChar(196), QLatin1String("\\\"{A}") })); QVERIFY(encodings.contains({ QChar(227), QLatin1String("\\~{a}") })); } + + void testIncludeKeywordLists() + { + Repository repo; + QTemporaryDir dir; + + // forge a syntax file + { + QVERIFY(QDir(dir.path()).mkpath(QLatin1String("syntax"))); + + const char syntax[] = R"xml( + + + + + c + a + + + b + a + + + c + b + + + + d + e##AAA + + + e + f + + + f + + + + + + + + + + + + + )xml"; + + QFile file(dir.path() + QLatin1String("/syntax/a.xml")); + QVERIFY(file.open(QIODevice::NewOnly | QIODevice::WriteOnly)); + QTextStream stream(&file); + stream << syntax; + } + + repo.addCustomSearchPath(dir.path()); + auto def = repo.definitionForName(QLatin1String("AAA")); + QCOMPARE(def.name(), QLatin1String("AAA")); + + auto klist1 = def.keywordList(QLatin1String("a")); + auto klist2 = def.keywordList(QLatin1String("b")); + auto klist3 = def.keywordList(QLatin1String("c")); + + // internal QHash is arbitrarily ordered and undeterministic + auto& klist = klist1.size() == 3 ? klist1 + : klist2.size() == 3 ? klist2 + : klist3; + + QCOMPARE(klist.size(), 3); + QVERIFY(klist.contains(QLatin1String("a"))); + QVERIFY(klist.contains(QLatin1String("b"))); + QVERIFY(klist.contains(QLatin1String("c"))); + + klist = def.keywordList(QLatin1String("d")); + QCOMPARE(klist.size(), 3); + QVERIFY(klist.contains(QLatin1String("d"))); + QVERIFY(klist.contains(QLatin1String("e"))); + QVERIFY(klist.contains(QLatin1String("f"))); + + klist = def.keywordList(QLatin1String("e")); + QCOMPARE(klist.size(), 2); + QVERIFY(klist.contains(QLatin1String("e"))); + QVERIFY(klist.contains(QLatin1String("f"))); + + klist = def.keywordList(QLatin1String("f")); + QCOMPARE(klist.size(), 1); + QVERIFY(klist.contains(QLatin1String("f"))); + } }; } diff --git a/data/schema/language.xsd b/data/schema/language.xsd --- a/data/schema/language.xsd +++ b/data/schema/language.xsd @@ -279,17 +279,23 @@ --> - - - + + + + + + diff --git a/data/syntax/scss.xml b/data/syntax/scss.xml --- a/data/syntax/scss.xml +++ b/data/syntax/scss.xml @@ -38,616 +38,7 @@ - - align-content - align-items - alignment-baseline - align-self - all - animation - animation-delay - animation-direction - animation-duration - animation-fill-mode - animation-iteration-count - animation-name - animation-play-state - animation-timing-function - appearance - azimuth - backface-visibility - background - background-attachment - background-blend-mode - background-clip - background-color - background-image - background-image-transform - background-origin - background-position - background-repeat - background-size - baseline-shift - block-size - block-step - block-step-align - block-step-insert - block-step-round - block-step-size - bookmark-label - bookmark-level - bookmark-state - border - border-block - border-block-color - border-block-end - border-block-end-color - border-block-end-style - border-block-end-width - border-block-start - border-block-start-color - border-block-start-style - border-block-start-width - border-block-style - border-block-width - border-bottom - border-bottom-color - border-bottom-left-radius - border-bottom-right-radius - border-bottom-style - border-bottom-width - border-boundary - border-collapse - border-color - border-image - border-image-outset - border-image-repeat - border-image-slice - border-image-source - border-image-transform - border-image-width - border-inline - border-inline-color - border-inline-end - border-inline-end-color - border-inline-end-style - border-inline-end-width - border-inline-start - border-inline-start-color - border-inline-start-style - border-inline-start-width - border-inline-style - border-inline-width - border-left - border-left-color - border-left-style - border-left-width - border-radius - border-right - border-right-color - border-right-style - border-right-width - border-spacing - border-style - border-top - border-top-color - border-top-left-radius - border-top-right-radius - border-top-style - border-top-width - border-width - bottom - box-decoration-break - box-shadow - box-sizing - box-snap - break-after - break-before - break-inside - caption-side - caret - caret-color - caret-shape - clear - clip - clip-path - clip-rule - color - color-adjust - color-interpolation-filters - columns - column-count - column-fill - column-gap - column-rule - column-rule-color - column-rule-style - column-rule-width - column-span - column-width - contain - content - continue - counter-increment - counter-reset - counter-set - cue - cue-after - cue-before - cursor - direction - display - dominant-baseline - elevation - empty-cells - fill - fill-break - fill-color - fill-image - fill-opacity - fill-origin - fill-position - fill-repeat - fill-rule - fill-size - filter - flex - flex-basis - flex-direction - flex-flow - flex-grow - flex-shrink - flex-wrap - float - float-defer - float-offset - float-reference - flood-color - flood-opacity - flow-from - flow-into - font - font-family - font-feature-settings - font-kerning - font-language-override - font-max-size - font-min-size - font-optical-sizing - font-palette - font-size - font-size-adjust - font-stretch - font-style - font-synthesis - font-variant - font-variant-alternates - font-variant-caps - font-variant-east-asian - font-variant-emoji - font-variant-ligatures - font-variant-numeric - font-variant-position - font-variation-settings - font-weight - footnote-display - footnote-policy - gap - glyph-orientation-vertical - grid - grid-area - grid-auto-columns - grid-auto-flow - grid-auto-rows - grid-column - grid-column-end - grid-column-gap - grid-column-start - grid-gap - grid-row - grid-row-end - grid-row-gap - grid-row-start - grid-template - grid-template-areas - grid-template-columns - grid-template-rows - hanging-punctuation - height - hyphenate-character - hyphenate-limit-chars - hyphenate-limit-last - hyphenate-limit-lines - hyphenate-limit-zone - hyphens - image-orientation - image-rendering - image-resolution - initial-letter - initial-letter-align - initial-letter-wrap - inline-size - inset - inset-block - inset-block-end - inset-block-start - inset-inline - inset-inline-end - inset-inline-start - isolation - justify-content - justify-items - justify-self - left - letter-spacing - lighting-color - line-break - line-grid - line-height - line-height-step - line-snap - list-style - list-style-image - list-style-position - list-style-type - margin - margin-block - margin-block-end - margin-block-start - margin-bottom - margin-inline - margin-inline-end - margin-inline-start - margin-left - margin-right - margin-top - marker - marker-end - marker-knockout-left - marker-knockout-right - marker-mid - marker-pattern - marker-segment - marker-side - marker-start - marquee-direction - marquee-loop - marquee-speed - marquee-style - mask - mask-border - mask-border-mode - mask-border-outset - mask-border-repeat - mask-border-slice - mask-border-source - mask-border-width - mask-clip - mask-composite - mask-image - mask-mode - mask-origin - mask-position - mask-repeat - mask-size - mask-type - max-block-size - max-height - max-inline-size - max-lines - max-width - min-block-size - min-height - min-inline-size - min-width - mix-blend-mode - nav-up - nav-down - nav-left - nav-right - object-fit - object-position - offset - offset-after - offset-anchor - offset-before - offset-distance - offset-end - offset-path - offset-position - offset-rotate - offset-start - opacity - order - orphans - outline - outline-color - outline-offset - outline-style - outline-width - overflow - overflow-style - overflow-wrap - overflow-x - overflow-y - padding - padding-block - padding-block-end - padding-block-start - padding-bottom - padding-inline - padding-inline-end - padding-inline-start - padding-left - padding-right - padding-top - page - page-break-after - page-break-before - page-break-inside - pause - pause-after - pause-before - perspective - perspective-origin - pitch - pitch-range - place-content - place-items - place-self - play-during - pointer-events - position - quotes - region-fragment - resize - richness - right - rotation - rotation-point - row-gap - ruby-align - ruby-merge - ruby-position - running - scrollbar-gutter - shape-image-threshold - shape-inside - shape-margin - shape-outside - size - speak - speak-header - speak-numeral - speak-punctuation - speech-rate - stress - string-set - table-layout - tab-size - text-align-all - text-align - text-align-last - text-combine-upright - text-decoration-color - text-decoration - text-decoration-line - text-decoration-skip - text-decoration-skip-ink - text-decoration-style - text-decoration-width - text-emphasis - text-emphasis-color - text-emphasis-position - text-emphasis-skip - text-emphasis-style - text-indent - text-justify - text-orientation - text-overflow - text-shadow - text-space-collapse - text-space-trim - text-spacing - text-transform - text-underline-offset - text-underline-position - text-wrap - top - transform - transform-box - transform-origin - transform-style - transition - transition-delay - transition-duration - transition-property - transition-timing-function - unicode-bidi - user-select - vertical-align - visibility - voice-family - volume - white-space - widows - width - will-change - word-break - word-spacing - word-wrap - wrap-after - wrap-before - wrap-flow - wrap-inside - wrap-through - writing-mode - z-index - - - -moz-animation - -moz-animation-delay - -moz-animation-direction - -moz-animation-duration - -moz-animation-fill-mode - -moz-animation-iteration-count - -moz-animation-name - -moz-animation-play-state - -moz-animation-timing-function - -moz-appearance - -moz-background-clip - -moz-background-origin - -moz-background-size - -moz-border-image - -moz-border-radius - -moz-border-radius-bottomleft - -moz-border-radius-bottomright - -moz-border-radius-topleft - -moz-border-radius-topright - -moz-box-align - -moz-box-direction - -moz-box-flex - -moz-box-flex-group - -moz-box-ordinal-group - -moz-box-orient - -moz-box-pack - -moz-box-shadow - -moz-box-sizing - -moz-box - -moz-column-count - -moz-column-fill - -moz-column-gap - -moz-column-rule - -moz-column-rule-color - -moz-column-rule-style - -moz-column-rule-width - -moz-columns - -moz-column-width - -moz-hyphens - -moz-opacity - -moz-outline-style - -moz-perspective - -moz-resize - -moz-text-align-last - -moz-text-decoration-color - -moz-text-decoration-line - -moz-text-decoration-style - -moz-transform - -moz-transform-origin - -moz-transform-style - -moz-transition - -moz-transition-delay - -moz-transition-duration - -moz-transition-property - -moz-transition-timing-function - -moz-user-select - - - -o-background-size - -o-linear-gradient - -o-text-overflow - -o-transition - -o-transform-origin - - - konq_bgpos_x - konq_bgpos_y - -khtml-background-size - -khtml-border-top-left-radius - -khtml-border-top-right-radius - -khtml-border-bottom-left-radius - -khtml-border-bottom-right-radius - -khtml-border-radius - -khtml-box-shadow - -khtml-opacity - - - -webkit-appearance - -webkit-animation - -webkit-animation-name - -webkit-animation-duration - -webkit-animation-iteration - -webkit-animation-direction - -webkit-animation-delay - -webkit-animation-play-state - -webkit-animation-fill-mode - -webkit-background-size - -webkit-backface-visibility - -webkit-border-image - -webkit-border-bottom-colors - -webkit-border-left-colors - -webkit-border-radius - -webkit-border-right-colors - -webkit-border-top-colors - -webkit-border-top-left-radius - -webkit-border-top-right-radius - -webkit-border-bottom-left-radius - -webkit-border-bottom-right-radius - -webkit-border-radius-bottomleft - -webkit-border-radius-bottomright - -webkit-box-align - -webkit-box-direction - -webkit-box-flex - -webkit-box-ordinal-group - -webkit-box-orient - -webkit-box-pack - -webkit-box-reflect - -webkit-box-shadow - -webkit-box-sizing - -webkit-column-count - -webkit-column-gap - -webkit-hyphens - -webkit-linear-gradient - -webkit-gradient - -webkit-overflow-scrolling - -webkit-perspective - -webkit-text-decoration - -webkit-text-decoration-skip - -webkit-text-fill-color - -webkit-text-stroke-color - -webkit-text-stroke-width - -webkit-text-size-adjust - -webkit-tap-highlight-color - -webkit-transform - -webkit-transform-origin - -webkit-transform-style - -webkit-transition - -webkit-transition-property - -webkit-transition-delay - -webkit-transition-duration - -webkit-user-select - - - zoom - -ms-animation-name - -ms-animation-duration - -ms-animation-iteration - -ms-animation-direction - -ms-animation-delay - -ms-animation-play-state - -ms-animation-fill-mode - -ms-box-sizing - -ms-filter - -ms-flex - -ms-flex-align - -ms-flex-direction - -ms-flex-flow - -ms-flex-item-align - -ms-flex-line-pack - -ms-flex-negative - -ms-flex-order - -ms-flex-pack - -ms-flex-positive - -ms-flex-position - -ms-flex-preferred-size - -ms-flex-wrap - -ms-interpolation-mode - -ms-linear-gradient - -ms-overflow-style - -ms-text-size-adjust - -ms-transform - -ms-transition - -ms-user-select + properties##CSS @@ -923,396 +314,20 @@ - inherit - unset - auto - + value keywords##CSS - none - hidden - dotted - dashed - solid - double - groove - ridge - inset - outset - xx-small - x-small - small - medium - large - x-large - xx-large - smaller - larger - italic - oblique - small-caps - normal - bold - bolder - lighter - light - transparent - repeat - repeat-x - repeat-y - no-repeat - baseline - sub - super - top - text-top - middle - bottom - text-bottom - left - right - center - justify - konq-center - disc - circle - square - box - decimal - decimal-leading-zero - lower-roman - upper-roman - lower-greek - lower-alpha - lower-latin - upper-alpha - upper-latin - hebrew - armenian - georgian - cjk-ideographic - hiragana - katakana - hiragana-iroha - katakana-iroha - inline - inline-block - block - list-item - run-in - compact - marker - table - inline-table - table-row-group - table-header-group - table-footer-group - table-row - table-column-group - table-column - table-cell - table-caption - crosshair - default - pointer - move - e-resize - ne-resize - nw-resize - n-resize - se-resize - sw-resize - s-resize - w-resize - text - wait - help - above - absolute - always - avoid - below - bidi-override - blink - both - capitalize - caption - clip - close-quote - collapse - condensed - crop - cross - ellipsis - ellipsis-word - embed - expanded - extra-condensed - extra-expanded - fixed - hand - hide - higher - icon - inside - invert - landscape - level - line-through - loud - lower - lowercase - ltr - menu - message-box - mix - narrower - no-close-quote - no-open-quote - nowrap - open-quote - outside - overline - portrait - pre - pre-line - pre-wrap - relative - rtl - scroll - semi-condensed - semi-expanded - separate - show - small-caption - static - static-position - status-bar - thick - thin - ultra-condensed - ultra-expanded - underline - uppercase - visible - wider - break - serif - sans-serif - cursive - fantasy - monospace - border-box - content-box - -epub-hyphens - contain - cover - - - all - ease - ease-in - ease-out - ease-in-out - step-start - step-end - linear - - - infinite - reverse - alternate - alternate-reverse - forwards - backwards - running - paused + values##CSS - black - silver - gray - white - maroon - red - purple - fuchsia - green - lime - olive - yellow - navy - blue - teal - aqua - orange - aliceblue - antiquewhite - aquamarine - azure - beige - bisque - blanchedalmond - blueviolet - brown - burlywood - cadetblue - chartreuse - chocolate - coral - cornflowerblue - cornsilk - crimson - cyan - aqua - darkblue - darkcyan - darkgoldenrod - darkgray - darkgreen - darkgrey - darkkhaki - darkmagenta - darkolivegreen - darkorange - darkorchid - darkred - darksalmon - darkseagreen - darkslateblue - darkslategray - darkslategrey - darkturquoise - darkviolet - deeppink - deepskyblue - dimgray - dimgrey - dodgerblue - firebrick - floralwhite - forestgreen - gainsboro - ghostwhite - gold - goldenrod - greenyellow - grey - honeydew - hotpink - indianred - indigo - ivory - khaki - lavender - lavenderblush - lawngreen - lemonchiffon - lightblue - lightcoral - lightcyan - lightgoldenrodyellow - lightgray - lightgreen - lightgrey - lightpink - lightsalmon - lightseagreen - lightskyblue - lightslategray - lightslategrey - lightsteelblue - lightyellow - limegreen - linen - magenta - fuchsia - mediumaquamarine - mediumblue - mediumorchid - mediumpurple - mediumseagreen - mediumslateblue - mediumspringgreen - mediumturquoise - mediumvioletred - midnightblue - mintcream - mistyrose - moccasin - navajowhite - oldlace - olivedrab - orangered - orchid - palegoldenrod - palegreen - paleturquoise - palevioletred - papayawhip - peachpuff - peru - pink - plum - powderblue - rosybrown - royalblue - saddlebrown - salmon - sandybrown - seagreen - seashell - sienna - skyblue - slateblue - slategray - slategrey - snow - springgreen - steelblue - tan - thistle - tomato - turquoise - violet - wheat - whitesmoke - yellowgreen - rebeccapurple - - ActiveBorder - ActiveCaption - AppWorkspace - Background - ButtonFace - ButtonHighlight - ButtonShadow - ButtonText - CaptionText - GrayText - Highlight - HighlightText - InactiveBorder - InactiveCaption - InactiveCaptionText - InfoBackground - InfoText - Menu - MenuText - Scrollbar - ThreeDDarkShadow - ThreeDFace - ThreeDHighlight - ThreeDLightShadow - ThreeDShadow - Window - WindowFrame - WindowText + colors##CSS + functions##CSS + red green @@ -1391,239 +406,32 @@ get-function if unique-id - - - attr - calc - hsl - hsla - linear-gradient - radial-gradient - repeating-linear-gradient - repeating-radial-gradient - rgb - rgba - var - url - - - rect - - - inset - circle - ellipse - polygon - - - blur - brightness - contrast - drop-shadow - grayscale - hue-rotate - invert - opacity - saturate - sepia - - - max-content - min-content - minmax - fix-content - repeat - - - cubic-bezier - frames - steps - - - matrix - matrix3d - perspective - rotate - rotate3d - rotateX - rotateY - rotateZ - scale - scale3d - scaleX - scaleY - scaleZ - skew - skewX - skewY - translate - translate3d - translateX - translateY - translateZ - - - local - format - - all - print - screen - speech - - - any-pointer - any-hover - aspect-ratio - color - color-gamut - color-index - display-mode - grid - height - hover - max-aspect-ratio - max-color - max-color-index - max-device-aspect-ratio - max-device-height - max-device-width - max-height - max-monochrome - max-resolution - max-width - min-aspect-ratio - min-color - min-color-index - min-device-aspect-ratio - min-device-height - min-device-width - min-height - min-monochrome - min-resolution - min-width - monochrome - orientation - pointer - resolution - scan - update - width + medias##CSS - after - before - cue - first-letter - first-line - selection - - - backdrop - placeholder - - slotted - - - - - value - choices - repeat-item - repeat-index - - -moz-progress-bar - -moz-range-progress - -moz-range-thumb - -moz-range-track - -moz-selection - -ms-fill - -ms-fill-lower - -ms-fill-upper - -ms-thumb - -ms-track - -webkit-progress-bar - -webkit-progress-value - -webkit-slider-runnable-track - -webkit-slider-thumb + pseudoelements##CSS - active - any-link - checked - default - defined - - disabled - empty - enabled - first-child - first-of-type - fullscreen - focus - focus-within - host - hover - in-range - indeterminate - invalid - lang - last-child - last-of-type - link - not - nth-child - nth-last-child - nth-last-of-type - nth-of-type - only-child - only-of-type - optional - out-of-range - placeholder-shown - read-only - read-write - required - root - scope - target - valid - visited - - - after - before - cue - first-letter - first-line - selection + pseudoclasses##CSS - not + pseudoclass-not##CSS - blank - first - left - recto - right - verso + pseudoclasses-@page##CSS - @character - @charset - @import - @namespace + at-rules##CSS @debug @@ -1634,9 +442,7 @@ - @document - @media - @supports + nested at-rules##CSS @at-rule @@ -1673,60 +479,35 @@ - min-width - max-width - width - min-height - max-height - height - zoom - min-zoom - max-zoom - user-zoom - orientation - viewport-fit + within-@viewport##CSS @page - size - marks - bleed + within-@page##CSS @font-face - font-display - font-family - font-stretch - font-style - font-weight - font-variant - font-feature-settings - font-variation-settings - src - unicode-range + within-@font-face##CSS @keyframes - from - to + within-@keyframes##CSS - and - only - not + media operators##CSS diff --git a/src/indexer/katehighlightingindexer.cpp b/src/indexer/katehighlightingindexer.cpp --- a/src/indexer/katehighlightingindexer.cpp +++ b/src/indexer/katehighlightingindexer.cpp @@ -193,6 +193,82 @@ return true; } +/** + * Helper class to search for non-existing keyword include. + */ +class KeywordIncludeChecker +{ +public: + void processElement(const QString &hlFilename, const QString &hlName, QXmlStreamReader &xml) + { + if (xml.name() == QLatin1String("list")) { + auto &keywords = m_keywordMap[hlName]; + keywords.filename = hlFilename; + auto name = xml.attributes().value(QLatin1String("name")).toString(); + m_currentIncludes = &keywords.includes[name]; + } + else if (xml.name() == QLatin1String("include")) { + if (!m_currentIncludes) { + qWarning() << hlFilename << "line" << xml.lineNumber() << " tag ouside "; + m_success = false; + } else { + m_currentIncludes->push_back({xml.lineNumber(), xml.readElementText()}); + } + } + } + + bool check() const + { + bool success = m_success; + for (auto &keywords : m_keywordMap) { + QMapIterator> includes(keywords.includes); + while (includes.hasNext()) { + includes.next(); + for (auto &include : includes.value()) { + bool containsKeywordName = true; + int const idx = include.name.indexOf(QStringLiteral("##")); + if (idx == -1) { + auto &keywordName = includes.key(); + containsKeywordName = keywords.includes.contains(keywordName); + } + else { + auto defName = include.name.mid(idx + 2); + auto listName = include.name.left(idx); + auto it = m_keywordMap.find(defName); + if (it == m_keywordMap.end()) { + qWarning() << keywords.filename << "line" << include.line << "unknown definition in" << include.name; + success = false; + } else { + containsKeywordName = it->includes.contains(listName); + } + } + + if (!containsKeywordName) { + qWarning() << keywords.filename << "line" << include.line << "unknown keyword name in" << include.name; + success = false; + } + } + } + } + return success; + } + +private: + struct Keywords + { + QString filename; + struct Include + { + qint64 line; + QString name; + }; + QMap> includes; + }; + QHash m_keywordMap; + QVector *m_currentIncludes = nullptr; + bool m_success = true; +}; + /** * Helper class to search for non-existing or unreferenced keyword lists. */ @@ -457,6 +533,7 @@ // index all given highlightings ContextChecker contextChecker; + KeywordIncludeChecker keywordIncludeChecker; QVariantMap hls; int anyError = 0; foreach (const QString &hlFilename, hlFilenames) { @@ -528,6 +605,9 @@ // search for used/existing contexts if applicable contextChecker.processElement(hlFilename, hlName, xml); + // search for existing keyword includes + keywordIncludeChecker.processElement(hlFilename, hlName, xml); + // search for used/existing attributes if applicable attributeChecker.processElement(xml); @@ -571,6 +651,9 @@ if (!contextChecker.check()) anyError = 7; + if (!keywordIncludeChecker.check()) + anyError = 7; + // bail out if any problem was seen if (anyError) diff --git a/src/lib/definition.cpp b/src/lib/definition.cpp --- a/src/lib/definition.cpp +++ b/src/lib/definition.cpp @@ -31,6 +31,7 @@ #include "context_p.h" #include "format.h" #include "format_p.h" +#include "repository.h" #include "repository_p.h" #include "rule_p.h" #include "ksyntaxhighlighting_logging.h" @@ -222,13 +223,13 @@ QStringList Definition::keywordLists() const { - d->load(); + d->load(DefinitionData::OnlyKeywords(true)); return d->keywordLists.keys(); } QStringList Definition::keywordList(const QString& name) const { - d->load(); + d->load(DefinitionData::OnlyKeywords(true)); const auto list = d->keywordList(name); return list ? list->keywords() : QStringList(); } @@ -357,14 +358,17 @@ return !contexts.isEmpty(); } -bool DefinitionData::load() +bool DefinitionData::load(OnlyKeywords onlyKeywords) { if (fileName.isEmpty()) return false; if (isLoaded()) return true; + if (bool(onlyKeywords) && keywordIsLoaded) + return true; + QFile file(fileName); if (!file.open(QFile::ReadOnly)) return false; @@ -375,15 +379,20 @@ if (token != QXmlStreamReader::StartElement) continue; - if (reader.name() == QLatin1String("highlighting")) - loadHighlighting(reader); + if (reader.name() == QLatin1String("highlighting")) { + loadHighlighting(reader, onlyKeywords); + if (bool(onlyKeywords)) { + return true; + } + } else if (reader.name() == QLatin1String("general")) loadGeneral(reader); } - for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) - (*it).setCaseSensitivity(caseSensitive); + for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) { + it->setCaseSensitivity(caseSensitive); + } foreach (auto context, contexts) { context->resolveContexts(); @@ -492,19 +501,31 @@ return true; } -void DefinitionData::loadHighlighting(QXmlStreamReader& reader) +void DefinitionData::loadHighlighting(QXmlStreamReader& reader, OnlyKeywords onlyKeywords) { Q_ASSERT(reader.name() == QLatin1String("highlighting")); Q_ASSERT(reader.tokenType() == QXmlStreamReader::StartElement); + // skip highlighting + reader.readNext(); + while (!reader.atEnd()) { switch (reader.tokenType()) { case QXmlStreamReader::StartElement: if (reader.name() == QLatin1String("list")) { - KeywordList keywords; - keywords.load(reader); - keywordLists.insert(keywords.name(), keywords); + if (!keywordIsLoaded) { + KeywordList keywords; + keywords.load(reader); + keywordLists.insert(keywords.name(), keywords); + } + else { + reader.skipCurrentElement(); + } + } else if (bool(onlyKeywords)) { + resolveIncludeKeywords(); + return; } else if (reader.name() == QLatin1String("contexts")) { + resolveIncludeKeywords(); loadContexts(reader); reader.readNext(); } else if (reader.name() == QLatin1String("itemDatas")) { @@ -522,6 +543,19 @@ } } +void DefinitionData::resolveIncludeKeywords() +{ + if (keywordIsLoaded) { + return; + } + + keywordIsLoaded = true; + + for (auto it = keywordLists.begin(); it != keywordLists.end(); ++it) { + it->resolveIncludeKeywords(*this); + } +} + void DefinitionData::loadContexts(QXmlStreamReader& reader) { Q_ASSERT(reader.name() == QLatin1String("contexts")); diff --git a/src/lib/definition_p.h b/src/lib/definition_p.h --- a/src/lib/definition_p.h +++ b/src/lib/definition_p.h @@ -54,17 +54,21 @@ void clear(); - bool load(); + enum class OnlyKeywords : bool; + + bool load(OnlyKeywords onlyKeywords = OnlyKeywords(false)); bool loadLanguage(QXmlStreamReader &reader); - void loadHighlighting(QXmlStreamReader &reader); + void loadHighlighting(QXmlStreamReader &reader, OnlyKeywords onlyKeywords); void loadContexts(QXmlStreamReader &reader); void loadItemData(QXmlStreamReader &reader); void loadGeneral(QXmlStreamReader &reader); void loadComments(QXmlStreamReader &reader); void loadFoldingIgnoreList(QXmlStreamReader &reader); void loadSpellchecking(QXmlStreamReader &reader); bool checkKateVersion(const QStringRef &verStr); + void resolveIncludeKeywords(); + KeywordList *keywordList(const QString &name); bool isWordDelimiter(QChar c) const; @@ -83,6 +87,7 @@ QHash formats; QString wordDelimiters; QString wordWrapDelimiters; + bool keywordIsLoaded = false; bool hasFoldingRegions = false; bool indentationBasedFolding = false; QStringList foldingIgnoreList; diff --git a/src/lib/keywordlist.cpp b/src/lib/keywordlist.cpp --- a/src/lib/keywordlist.cpp +++ b/src/lib/keywordlist.cpp @@ -22,6 +22,9 @@ */ #include "keywordlist_p.h" +#include "repository.h" +#include "definition_p.h" +#include "ksyntaxhighlighting_logging.h" #include #include @@ -58,6 +61,11 @@ reader.readNextStartElement(); break; } + else if (reader.name() == QLatin1String("include")) { + m_includes.append(reader.readElementText().trimmed()); + reader.readNextStartElement(); + break; + } reader.readNext(); break; case QXmlStreamReader::EndElement: @@ -102,3 +110,40 @@ */ std::sort(vectorToSort.begin(), vectorToSort.end(), [caseSensitive] (const QStringRef &a, const QStringRef &b) { return a.compare(b, caseSensitive) < 0; }); } + +void KeywordList::resolveIncludeKeywords(DefinitionData &def) +{ + while (!m_includes.isEmpty()) { + const auto kw_include = std::move(m_includes.back()); + m_includes.pop_back(); + + const auto idx = kw_include.indexOf(QLatin1String("##")); + KeywordList *keywords = nullptr; + + if (idx >= 0) { + auto listName = kw_include.left(idx); + auto defName = kw_include.mid(idx + 2); + auto includeDef = def.repo->definitionForName(defName); + if (includeDef.isValid()) { + auto defData = DefinitionData::get(includeDef); + defData->load(DefinitionData::OnlyKeywords(true)); + keywords = defData->keywordList(listName); + } + else { + qCWarning(Log) << "Unable to resolve external include keyword for definition" << defName << "in" << def.name; + } + } else { + keywords = def.keywordList(kw_include); + } + + if (keywords) { + if (this != keywords) { + keywords->resolveIncludeKeywords(def); + } + m_keywords += keywords->m_keywords; + } + else { + qCWarning(Log) << "Unresolved include keyword" << kw_include << "in" << def.name; + } + } +} diff --git a/src/lib/keywordlist_p.h b/src/lib/keywordlist_p.h --- a/src/lib/keywordlist_p.h +++ b/src/lib/keywordlist_p.h @@ -34,6 +34,9 @@ namespace KSyntaxHighlighting { +class Repository; +class DefinitionData; + class KeywordList { public: @@ -67,6 +70,7 @@ void load(QXmlStreamReader &reader); void setCaseSensitivity(Qt::CaseSensitivity caseSensitive); void initLookupForCaseSensitivity(Qt::CaseSensitivity caseSensitive); + void resolveIncludeKeywords(DefinitionData &def); private: /** @@ -79,6 +83,11 @@ */ QStringList m_keywords; + /** + * raw list of include keywords, as seen in XML (but trimmed) + */ + QStringList m_includes; + /** * default case-sensitivity setting */