| 1 | // Copyright (C) 2024 The Qt Company Ltd. | 
| 2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only | 
| 3 |  | 
| 4 | #ifndef QQMLSEMANTICTOKENS_P_H | 
| 5 | #define QQMLSEMANTICTOKENS_P_H | 
| 6 |  | 
| 7 | // | 
| 8 | //  W A R N I N G | 
| 9 | //  ------------- | 
| 10 | // | 
| 11 | // This file is not part of the Qt API.  It exists purely as an | 
| 12 | // implementation detail.  This header file may change from version to | 
| 13 | // version without notice, or even be removed. | 
| 14 | // | 
| 15 | // We mean it. | 
| 16 | // | 
| 17 |  | 
| 18 | #include <QtLanguageServer/private/qlanguageserverspec_p.h> | 
| 19 | #include <QtQmlDom/private/qqmldomitem_p.h> | 
| 20 | #include <QtCore/qlist.h> | 
| 21 | #include <QtCore/qmap.h> | 
| 22 |  | 
| 23 | QT_BEGIN_NAMESPACE | 
| 24 |  | 
| 25 | Q_DECLARE_LOGGING_CATEGORY(semanticTokens) | 
| 26 |  | 
| 27 | namespace HighlightingUtils { | 
| 28 | Q_NAMESPACE | 
| 29 |  | 
| 30 | // Protocol agnostic highlighting kinds | 
| 31 | // Use this enum while visiting dom tree to define the highlighting kinds for the semantic tokens | 
| 32 | // Then map it to the protocol specific token types and modifiers | 
| 33 | // This can be as much as detailed as needed | 
| 34 | enum class QmlHighlightKind { | 
| 35 |     QmlKeyword, // Qml keyword | 
| 36 |     QmlType, // Qml type name | 
| 37 |     QmlImportId, // Qml import module name | 
| 38 |     QmlNamespace, // Qml module namespace, i.e import QtQuick as Namespace | 
| 39 |     QmlLocalId, // Object id within the same file | 
| 40 |     QmlExternalId, // Object id defined in another file. [UNUSED FOR NOW] | 
| 41 |     QmlProperty, // Qml property. For now used for all kind of properties | 
| 42 |     QmlScopeObjectProperty, // Qml property defined in the current scope | 
| 43 |     QmlRootObjectProperty, // Qml property defined in the parent scopes | 
| 44 |     QmlExternalObjectProperty, // Qml property defined in the root object of another file | 
| 45 |     QmlMethod, | 
| 46 |     QmlMethodParameter, | 
| 47 |     QmlSignal, | 
| 48 |     QmlSignalHandler, | 
| 49 |     QmlEnumName, // Enum type name | 
| 50 |     QmlEnumMember, // Enum field names | 
| 51 |     QmlPragmaName, // Qml pragma name | 
| 52 |     QmlPragmaValue, // Qml pragma value | 
| 53 |     QmlTypeModifier, // list<QtObject>, list is the modifier, QtObject is the type | 
| 54 |     JsImport, // Js imported name | 
| 55 |     JsGlobalVar, // Js global variable or objects | 
| 56 |     JsGlobalMethod, // Js global method | 
| 57 |     JsScopeVar, // Js variable defined in the current scope | 
| 58 |     JsLabel, // js label | 
| 59 |     Number, | 
| 60 |     String, | 
| 61 |     , | 
| 62 |     Operator, | 
| 63 |     Unknown, // Used for the unknown tokens | 
| 64 | }; | 
| 65 |  | 
| 66 | enum class QmlHighlightModifier { | 
| 67 |     None = 0, | 
| 68 |     QmlPropertyDefinition = 1 << 0, | 
| 69 |     QmlDefaultProperty = 1 << 1, | 
| 70 |     QmlRequiredProperty = 1 << 2, | 
| 71 |     QmlReadonlyProperty = 1 << 3, | 
| 72 | }; | 
| 73 | Q_DECLARE_FLAGS(QmlHighlightModifiers, QmlHighlightModifier) | 
| 74 | Q_DECLARE_OPERATORS_FOR_FLAGS(QmlHighlightModifiers) | 
| 75 |  | 
| 76 | enum class HighlightingMode { Default, QtCHighlighting }; | 
| 77 |  | 
| 78 | // Protocol specific token types | 
| 79 | // The values in this enum are converted to relevant strings and sent to the client as server | 
| 80 | // capabilities The convention is that the first letter in the enum value is decapitalized and the | 
| 81 | // rest is unchanged i.e Namespace -> "namespace" This is handled in enumToByteArray() helper | 
| 82 | // function. | 
| 83 | enum class SemanticTokenProtocolTypes { | 
| 84 |     // Subset of the QLspSpefication::SemanticTokenTypes enum | 
| 85 |     // We register only the token types used in the qml semantic highlighting | 
| 86 |     Namespace, | 
| 87 |     Type, | 
| 88 |     Enum, | 
| 89 |     Parameter, | 
| 90 |     Variable, | 
| 91 |     Property, | 
| 92 |     EnumMember, | 
| 93 |     Method, | 
| 94 |     Keyword, | 
| 95 |     , | 
| 96 |     String, | 
| 97 |     Number, | 
| 98 |     Regexp, | 
| 99 |     Operator, | 
| 100 |     Decorator, | 
| 101 |  | 
| 102 |     // Additional token types for the extended semantic highlighting | 
| 103 |     QmlLocalId, // object id within the same file | 
| 104 |     QmlExternalId, // object id defined in another file | 
| 105 |     QmlRootObjectProperty, // qml property defined in the parent scopes | 
| 106 |     QmlScopeObjectProperty, // qml property defined in the current scope | 
| 107 |     QmlExternalObjectProperty, // qml property defined in the root object of another file | 
| 108 |     JsScopeVar, // js variable defined in the current file | 
| 109 |     JsImportVar, // js import name that is imported in the qml file | 
| 110 |     JsGlobalVar, // js global variables | 
| 111 |     QmlStateName, // name of a qml state | 
| 112 | }; | 
| 113 | Q_ENUM_NS(SemanticTokenProtocolTypes) | 
| 114 |  | 
| 115 | } // namespace HighlightingUtils | 
| 116 |  | 
| 117 | // Represents a semantic highlighting token | 
| 118 | // startLine and startColumn are 0-based as in LSP spec. | 
| 119 | struct Token | 
| 120 | { | 
| 121 |     Token() = default; | 
| 122 |     Token(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0) | 
| 123 |         : offset(loc.offset), | 
| 124 |           length(loc.length), | 
| 125 |           startLine(loc.startLine - 1), | 
| 126 |           startColumn(loc.startColumn - 1), | 
| 127 |           tokenType(tokenType), | 
| 128 |           tokenModifier(tokenModifier) | 
| 129 |     { | 
| 130 |     } | 
| 131 |  | 
| 132 |     inline friend bool operator<(const Token &lhs, const Token &rhs) | 
| 133 |     { | 
| 134 |         return lhs.offset < rhs.offset; | 
| 135 |     } | 
| 136 |  | 
| 137 |     inline friend bool operator==(const Token &lhs, const Token &rhs) | 
| 138 |     { | 
| 139 |         return lhs.offset == rhs.offset && lhs.length == rhs.length | 
| 140 |                 && lhs.startLine == rhs.startLine && lhs.startColumn == rhs.startColumn | 
| 141 |                 && lhs.tokenType == rhs.tokenType && lhs.tokenModifier == rhs.tokenModifier; | 
| 142 |     } | 
| 143 |  | 
| 144 |     int offset; | 
| 145 |     int length; | 
| 146 |     int startLine; | 
| 147 |     int startColumn; | 
| 148 |     int tokenType; | 
| 149 |     int tokenModifier; | 
| 150 | }; | 
| 151 |  | 
| 152 | using HighlightsContainer = QMap<int, QT_PREPEND_NAMESPACE(Token)>; | 
| 153 |  | 
| 154 | /*! | 
| 155 | \internal | 
| 156 | Offsets start from zero. | 
| 157 | */ | 
| 158 | struct HighlightsRange | 
| 159 | { | 
| 160 |     int startOffset; | 
| 161 |     int endOffset; | 
| 162 | }; | 
| 163 |  | 
| 164 | class Highlights | 
| 165 | { | 
| 166 | public: | 
| 167 |     using QmlHighlightKindToLspKind = int (*)(HighlightingUtils::QmlHighlightKind); | 
| 168 |     Highlights(HighlightingUtils::HighlightingMode mode = HighlightingUtils::HighlightingMode::Default); | 
| 169 |     void addHighlight(const QQmlJS::SourceLocation &loc, HighlightingUtils::QmlHighlightKind, | 
| 170 |                       HighlightingUtils::QmlHighlightModifiers = | 
| 171 |                               HighlightingUtils::QmlHighlightModifier::None); | 
| 172 |     HighlightsContainer &highlights() { return m_highlights; } | 
| 173 |     const HighlightsContainer &highlights() const { return m_highlights; } | 
| 174 |  | 
| 175 | private: | 
| 176 |     void addHighlightImpl(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0); | 
| 177 |     HighlightsContainer m_highlights; | 
| 178 |     QmlHighlightKindToLspKind m_mapToProtocol; | 
| 179 | }; | 
| 180 |  | 
| 181 | namespace HighlightingUtils | 
| 182 | { | 
| 183 |     QList<int> encodeSemanticTokens(Highlights &highlights); | 
| 184 |     QList<QQmlJS::SourceLocation> | 
| 185 |     sourceLocationsFromMultiLineToken(QStringView code, | 
| 186 |                                       const QQmlJS::SourceLocation &tokenLocation); | 
| 187 |     void addModifier(QLspSpecification::SemanticTokenModifiers modifier, int *baseModifier); | 
| 188 |     bool rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc, const HighlightsRange &r); | 
| 189 |     QList<QLspSpecification::SemanticTokensEdit> computeDiff(const QList<int> &, const QList<int> &); | 
| 190 |     void updateResultID(QByteArray &resultID); | 
| 191 |     QList<int> collectTokens(const QQmlJS::Dom::DomItem &item, | 
| 192 |                              const std::optional<HighlightsRange> &range, | 
| 193 |                              HighlightingMode mode = HighlightingMode::Default); | 
| 194 | } // namespace HighlightingUtils | 
| 195 |  | 
| 196 | class HighlightingVisitor | 
| 197 | { | 
| 198 | public: | 
| 199 |     HighlightingVisitor(Highlights &highlights, const std::optional<HighlightsRange> &range); | 
| 200 |     bool operator()(QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &item, bool); | 
| 201 |  | 
| 202 | private: | 
| 203 |     void (const QQmlJS::Dom::DomItem &item); | 
| 204 |     void highlightImport(const QQmlJS::Dom::DomItem &item); | 
| 205 |     void highlightBinding(const QQmlJS::Dom::DomItem &item); | 
| 206 |     void highlightPragma(const QQmlJS::Dom::DomItem &item); | 
| 207 |     void highlightEnumItem(const QQmlJS::Dom::DomItem &item); | 
| 208 |     void highlightEnumDecl(const QQmlJS::Dom::DomItem &item); | 
| 209 |     void highlightQmlObject(const QQmlJS::Dom::DomItem &item); | 
| 210 |     void highlightComponent(const QQmlJS::Dom::DomItem &item); | 
| 211 |     void highlightPropertyDefinition(const QQmlJS::Dom::DomItem &item); | 
| 212 |     void highlightMethod(const QQmlJS::Dom::DomItem &item); | 
| 213 |     void highlightScriptLiteral(const QQmlJS::Dom::DomItem &item); | 
| 214 |     void highlightIdentifier(const QQmlJS::Dom::DomItem &item); | 
| 215 |     void highlightBySemanticAnalysis(const QQmlJS::Dom::DomItem &item, QQmlJS::SourceLocation loc); | 
| 216 |     void highlightScriptExpressions(const QQmlJS::Dom::DomItem &item); | 
| 217 |  | 
| 218 | private: | 
| 219 |     Highlights &m_highlights; | 
| 220 |     std::optional<HighlightsRange> m_range; | 
| 221 | }; | 
| 222 |  | 
| 223 | QT_END_NAMESPACE | 
| 224 |  | 
| 225 | #endif // QQMLSEMANTICTOKENS_P_H | 
| 226 |  |