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
23QT_BEGIN_NAMESPACE
24
25Q_DECLARE_LOGGING_CATEGORY(semanticTokens)
26
27namespace HighlightingUtils {
28Q_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
34enum 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 Comment,
62 Operator,
63 Field, // Used for the field names in the property chains,
64 Unknown, // Used for the unknown tokens
65};
66
67enum class QmlHighlightModifier {
68 None = 0,
69 QmlPropertyDefinition = 1 << 0,
70 QmlDefaultProperty = 1 << 1,
71 QmlFinalProperty = 1 << 2,
72 QmlRequiredProperty = 1 << 3,
73 QmlReadonlyProperty = 1 << 4,
74};
75Q_DECLARE_FLAGS(QmlHighlightModifiers, QmlHighlightModifier)
76Q_DECLARE_OPERATORS_FOR_FLAGS(QmlHighlightModifiers)
77
78enum class HighlightingMode { Default, QtCHighlighting };
79
80// Protocol specific token types
81// The values in this enum are converted to relevant strings and sent to the client as server
82// capabilities The convention is that the first letter in the enum value is decapitalized and the
83// rest is unchanged i.e Namespace -> "namespace" This is handled in enumToByteArray() helper
84// function.
85enum class SemanticTokenProtocolTypes {
86 // Subset of the QLspSpefication::SemanticTokenTypes enum
87 // We register only the token types used in the qml semantic highlighting
88 Namespace,
89 Type,
90 Enum,
91 Parameter,
92 Variable,
93 Property,
94 EnumMember,
95 Method,
96 Keyword,
97 Comment,
98 String,
99 Number,
100 Regexp,
101 Operator,
102 Decorator,
103
104 // Additional token types for the extended semantic highlighting
105 QmlLocalId, // object id within the same file
106 QmlExternalId, // object id defined in another file
107 QmlRootObjectProperty, // qml property defined in the parent scopes
108 QmlScopeObjectProperty, // qml property defined in the current scope
109 QmlExternalObjectProperty, // qml property defined in the root object of another file
110 JsScopeVar, // js variable defined in the current file
111 JsImportVar, // js import name that is imported in the qml file
112 JsGlobalVar, // js global variables
113 QmlStateName, // name of a qml state
114 Field, // used for the field names in the property chains
115 Unknown, // used for the unknown contexts
116};
117Q_ENUM_NS(SemanticTokenProtocolTypes)
118
119} // namespace HighlightingUtils
120
121// Represents a semantic highlighting token
122// startLine and startColumn are 0-based as in LSP spec.
123struct Token
124{
125 Token() = default;
126 Token(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0)
127 : offset(loc.offset),
128 length(loc.length),
129 startLine(loc.startLine - 1),
130 startColumn(loc.startColumn - 1),
131 tokenType(tokenType),
132 tokenModifier(tokenModifier)
133 {
134 }
135
136 inline friend bool operator<(const Token &lhs, const Token &rhs)
137 {
138 return lhs.offset < rhs.offset;
139 }
140
141 inline friend bool operator==(const Token &lhs, const Token &rhs)
142 {
143 return lhs.offset == rhs.offset && lhs.length == rhs.length
144 && lhs.startLine == rhs.startLine && lhs.startColumn == rhs.startColumn
145 && lhs.tokenType == rhs.tokenType && lhs.tokenModifier == rhs.tokenModifier;
146 }
147
148 int offset;
149 int length;
150 int startLine;
151 int startColumn;
152 int tokenType;
153 int tokenModifier;
154};
155
156using HighlightsContainer = QMap<int, QT_PREPEND_NAMESPACE(Token)>;
157
158/*!
159\internal
160Offsets start from zero.
161*/
162struct HighlightsRange
163{
164 int startOffset;
165 int endOffset;
166};
167
168class Highlights
169{
170public:
171 using QmlHighlightKindToLspKind = int (*)(HighlightingUtils::QmlHighlightKind);
172 Highlights(HighlightingUtils::HighlightingMode mode = HighlightingUtils::HighlightingMode::Default);
173 void addHighlight(const QQmlJS::SourceLocation &loc, HighlightingUtils::QmlHighlightKind,
174 HighlightingUtils::QmlHighlightModifiers =
175 HighlightingUtils::QmlHighlightModifier::None);
176 HighlightsContainer &tokens() { return m_highlights; }
177 const HighlightsContainer &tokens() const { return m_highlights; }
178
179private:
180 void addHighlightImpl(const QQmlJS::SourceLocation &loc, int tokenType, int tokenModifier = 0);
181 HighlightsContainer m_highlights;
182 QmlHighlightKindToLspKind m_mapToProtocol;
183};
184
185namespace HighlightingUtils
186{
187 QList<int> encodeSemanticTokens(Highlights &highlights);
188 QList<QQmlJS::SourceLocation>
189 sourceLocationsFromMultiLineToken(QStringView code,
190 const QQmlJS::SourceLocation &tokenLocation);
191 void addModifier(QLspSpecification::SemanticTokenModifiers modifier, int *baseModifier);
192 bool rangeOverlapsWithSourceLocation(const QQmlJS::SourceLocation &loc, const HighlightsRange &r);
193 QList<QLspSpecification::SemanticTokensEdit> computeDiff(const QList<int> &, const QList<int> &);
194 void updateResultID(QByteArray &resultID);
195 QList<int> collectTokens(const QQmlJS::Dom::DomItem &item,
196 const std::optional<HighlightsRange> &range,
197 HighlightingMode mode = HighlightingMode::Default);
198 Highlights visitTokens(const QQmlJS::Dom::DomItem &item,
199 const std::optional<HighlightsRange> &range,
200 HighlightingMode mode = HighlightingMode::Default);
201} // namespace HighlightingUtils
202
203class HighlightingVisitor
204{
205public:
206 HighlightingVisitor(const QQmlJS::Dom::DomItem &item,
207 const std::optional<HighlightsRange> &range,
208 HighlightingUtils::HighlightingMode mode =
209 HighlightingUtils::HighlightingMode::Default);
210 const Highlights &hightights() const { return m_highlights; }
211 Highlights &highlights() { return m_highlights; }
212
213private:
214 bool visitor(QQmlJS::Dom::Path, const QQmlJS::Dom::DomItem &item, bool);
215 void highlightComment(const QQmlJS::Dom::DomItem &item);
216 void highlightImport(const QQmlJS::Dom::DomItem &item);
217 void highlightBinding(const QQmlJS::Dom::DomItem &item);
218 void highlightPragma(const QQmlJS::Dom::DomItem &item);
219 void highlightEnumItem(const QQmlJS::Dom::DomItem &item);
220 void highlightEnumDecl(const QQmlJS::Dom::DomItem &item);
221 void highlightQmlObject(const QQmlJS::Dom::DomItem &item);
222 void highlightComponent(const QQmlJS::Dom::DomItem &item);
223 void highlightPropertyDefinition(const QQmlJS::Dom::DomItem &item);
224 void highlightMethod(const QQmlJS::Dom::DomItem &item);
225 void highlightScriptLiteral(const QQmlJS::Dom::DomItem &item);
226 void highlightIdentifier(const QQmlJS::Dom::DomItem &item);
227 void highlightBySemanticAnalysis(const QQmlJS::Dom::DomItem &item, QQmlJS::SourceLocation loc);
228 void highlightScriptExpressions(const QQmlJS::Dom::DomItem &item);
229 void highlightCallExpression(const QQmlJS::Dom::DomItem &item);
230 void highlightFieldMemberAccess(const QQmlJS::Dom::DomItem &item, QQmlJS::SourceLocation loc);
231
232private:
233 Highlights m_highlights;
234 std::optional<HighlightsRange> m_range;
235};
236
237QT_END_NAMESPACE
238
239#endif // QQMLSEMANTICTOKENS_P_H
240

source code of qtdeclarative/src/qmlls/qqmlsemantictokens_p.h