1 /****************************************************************************
2 **
3 ** Copyright (C) 2018 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of Qt Creator.
7 **
8 ** Commercial License Usage
9 ** Licensees holding valid commercial Qt licenses may use this file in
10 ** accordance with the commercial license agreement provided with the
11 ** Software or, alternatively, in accordance with the terms contained in
12 ** a written agreement between you and The Qt Company. For licensing terms
13 ** and conditions see https://www.qt.io/terms-conditions. For further
14 ** information use the contact form at https://www.qt.io/contact-us.
15 **
16 ** GNU General Public License Usage
17 ** Alternatively, this file may be used under the terms of the GNU
18 ** General Public License version 3 as published by the Free Software
19 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
20 ** included in the packaging of this file. Please review the following
21 ** information to ensure the GNU General Public License requirements will
22 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
23 **
24 ****************************************************************************/
25
26 #include "languagefeatures.h"
27
28 #include <cstddef>
29
30 namespace LanguageServerProtocol {
31
32 constexpr const char HoverRequest::methodName[];
33 constexpr const char GotoDefinitionRequest::methodName[];
34 constexpr const char GotoTypeDefinitionRequest::methodName[];
35 constexpr const char GotoImplementationRequest::methodName[];
36 constexpr const char FindReferencesRequest::methodName[];
37 constexpr const char DocumentHighlightsRequest::methodName[];
38 constexpr const char DocumentSymbolsRequest::methodName[];
39 constexpr const char CodeActionRequest::methodName[];
40 constexpr const char CodeLensRequest::methodName[];
41 constexpr const char CodeLensResolveRequest::methodName[];
42 constexpr const char DocumentLinkRequest::methodName[];
43 constexpr const char DocumentLinkResolveRequest::methodName[];
44 constexpr const char DocumentColorRequest::methodName[];
45 constexpr const char ColorPresentationRequest::methodName[];
46 constexpr const char DocumentFormattingRequest::methodName[];
47 constexpr const char DocumentRangeFormattingRequest::methodName[];
48 constexpr const char DocumentOnTypeFormattingRequest::methodName[];
49 constexpr const char RenameRequest::methodName[];
50 constexpr const char SignatureHelpRequest::methodName[];
51 constexpr const char SemanticHighlightNotification::methodName[];
52 constexpr const char PrepareRenameRequest::methodName[];
53
content() const54 HoverContent LanguageServerProtocol::Hover::content() const
55 {
56 return HoverContent(value(contentsKey));
57 }
58
setContent(const HoverContent & content)59 void Hover::setContent(const HoverContent &content)
60 {
61 if (auto val = Utils::get_if<MarkedString>(&content))
62 insert(contentsKey, *val);
63 else if (auto val = Utils::get_if<MarkupContent>(&content))
64 insert(contentsKey, *val);
65 else if (auto val = Utils::get_if<QList<MarkedString>>(&content))
66 insert(contentsKey, LanguageClientArray<MarkedString>(*val).toJson());
67 else
68 QTC_ASSERT_STRING("LanguageClient Using unknown type Hover::setContent");
69 }
70
HoverRequest(const TextDocumentPositionParams & params)71 HoverRequest::HoverRequest(const TextDocumentPositionParams ¶ms)
72 : Request(methodName, params)
73 { }
74
documentation() const75 Utils::optional<MarkupOrString> ParameterInformation::documentation() const
76 {
77 QJsonValue documentation = value(documentationKey);
78 if (documentation.isUndefined())
79 return Utils::nullopt;
80 return MarkupOrString(documentation);
81 }
82
GotoDefinitionRequest(const TextDocumentPositionParams & params)83 GotoDefinitionRequest::GotoDefinitionRequest(const TextDocumentPositionParams ¶ms)
84 : Request(methodName, params)
85 { }
86
GotoTypeDefinitionRequest(const TextDocumentPositionParams & params)87 GotoTypeDefinitionRequest::GotoTypeDefinitionRequest(const TextDocumentPositionParams ¶ms)
88 : Request(methodName, params)
89 { }
90
GotoImplementationRequest(const TextDocumentPositionParams & params)91 GotoImplementationRequest::GotoImplementationRequest(const TextDocumentPositionParams ¶ms)
92 : Request(methodName, params)
93 { }
94
FindReferencesRequest(const ReferenceParams & params)95 FindReferencesRequest::FindReferencesRequest(const ReferenceParams ¶ms)
96 : Request(methodName, params)
97 { }
98
DocumentHighlightsRequest(const TextDocumentPositionParams & params)99 DocumentHighlightsRequest::DocumentHighlightsRequest(const TextDocumentPositionParams ¶ms)
100 : Request(methodName, params)
101 { }
102
DocumentSymbolsRequest(const DocumentSymbolParams & params)103 DocumentSymbolsRequest::DocumentSymbolsRequest(const DocumentSymbolParams ¶ms)
104 : Request(methodName, params)
105 { }
106
only() const107 Utils::optional<QList<CodeActionKind> > CodeActionParams::CodeActionContext::only() const
108 {
109 return optionalArray<CodeActionKind>(onlyKey);
110 }
111
setOnly(const QList<CodeActionKind> & only)112 void CodeActionParams::CodeActionContext::setOnly(const QList<CodeActionKind> &only)
113 {
114 insertArray(onlyKey, only);
115 }
116
CodeActionRequest(const CodeActionParams & params)117 CodeActionRequest::CodeActionRequest(const CodeActionParams ¶ms)
118 : Request(methodName, params)
119 { }
120
CodeLensRequest(const CodeLensParams & params)121 CodeLensRequest::CodeLensRequest(const CodeLensParams ¶ms)
122 : Request(methodName, params)
123 { }
124
CodeLensResolveRequest(const CodeLens & params)125 CodeLensResolveRequest::CodeLensResolveRequest(const CodeLens ¶ms)
126 : Request(methodName, params)
127 { }
128
DocumentLinkRequest(const DocumentLinkParams & params)129 DocumentLinkRequest::DocumentLinkRequest(const DocumentLinkParams ¶ms)
130 : Request(methodName, params)
131 { }
132
DocumentLinkResolveRequest(const DocumentLink & params)133 DocumentLinkResolveRequest::DocumentLinkResolveRequest(const DocumentLink ¶ms)
134 : Request(methodName, params)
135 { }
136
DocumentColorRequest(const DocumentColorParams & params)137 DocumentColorRequest::DocumentColorRequest(const DocumentColorParams ¶ms)
138 : Request(methodName, params)
139 { }
140
ColorPresentationRequest(const ColorPresentationParams & params)141 ColorPresentationRequest::ColorPresentationRequest(const ColorPresentationParams ¶ms)
142 : Request(methodName, params)
143 { }
144
properties() const145 QHash<QString, DocumentFormattingProperty> FormattingOptions::properties() const
146 {
147 QHash<QString, DocumentFormattingProperty> ret;
148 for (const QString &key : keys()) {
149 if (key == tabSizeKey || key == insertSpaceKey)
150 continue;
151 QJsonValue property = value(key);
152 if (property.isBool())
153 ret[key] = property.toBool();
154 if (property.isDouble())
155 ret[key] = property.toDouble();
156 if (property.isString())
157 ret[key] = property.toString();
158 }
159 return ret;
160 }
161
setProperty(const QString & key,const DocumentFormattingProperty & property)162 void FormattingOptions::setProperty(const QString &key, const DocumentFormattingProperty &property)
163 {
164 using namespace Utils;
165 if (auto val = get_if<double>(&property))
166 insert(key, *val);
167 else if (auto val = get_if<QString>(&property))
168 insert(key, *val);
169 else if (auto val = get_if<bool>(&property))
170 insert(key, *val);
171 }
172
DocumentFormattingRequest(const DocumentFormattingParams & params)173 DocumentFormattingRequest::DocumentFormattingRequest(const DocumentFormattingParams ¶ms)
174 : Request(methodName, params)
175 { }
176
DocumentRangeFormattingRequest(const DocumentRangeFormattingParams & params)177 DocumentRangeFormattingRequest::DocumentRangeFormattingRequest(
178 const DocumentRangeFormattingParams ¶ms)
179 : Request(methodName, params)
180 { }
181
isValid() const182 bool DocumentOnTypeFormattingParams::isValid() const
183 {
184 return contains(textDocumentKey) && contains(positionKey) && contains(chKey)
185 && contains(optionsKey);
186 }
187
DocumentOnTypeFormattingRequest(const DocumentOnTypeFormattingParams & params)188 DocumentOnTypeFormattingRequest::DocumentOnTypeFormattingRequest(
189 const DocumentOnTypeFormattingParams ¶ms)
190 : Request(methodName, params)
191 { }
192
PrepareRenameRequest(const TextDocumentPositionParams & params)193 PrepareRenameRequest::PrepareRenameRequest(const TextDocumentPositionParams ¶ms)
194 : Request(methodName, params)
195 { }
196
isValid() const197 bool RenameParams::isValid() const
198 {
199 return contains(textDocumentKey) && contains(positionKey) && contains(newNameKey);
200 }
201
RenameRequest(const RenameParams & params)202 RenameRequest::RenameRequest(const RenameParams ¶ms)
203 : Request(methodName, params)
204 { }
205
target() const206 Utils::optional<DocumentUri> DocumentLink::target() const
207 {
208 Utils::optional<QString> optionalTarget = optionalValue<QString>(targetKey);
209 return optionalTarget.has_value()
210 ? Utils::make_optional(DocumentUri::fromProtocol(optionalTarget.value()))
211 : Utils::nullopt;
212 }
213
data() const214 Utils::optional<QJsonValue> DocumentLink::data() const
215 {
216 return contains(dataKey) ? Utils::make_optional(value(dataKey)) : Utils::nullopt;
217 }
218
TextDocumentParams()219 TextDocumentParams::TextDocumentParams()
220 : TextDocumentParams(TextDocumentIdentifier())
221 { }
222
TextDocumentParams(const TextDocumentIdentifier & identifier)223 TextDocumentParams::TextDocumentParams(const TextDocumentIdentifier &identifier)
224 : JsonObject()
225 {
226 setTextDocument(identifier);
227 }
228
GotoResult(const QJsonValue & value)229 GotoResult::GotoResult(const QJsonValue &value)
230 {
231 if (value.isArray()) {
232 QList<Location> locations;
233 for (auto arrayValue : value.toArray()) {
234 if (arrayValue.isObject())
235 locations.append(Location(arrayValue.toObject()));
236 }
237 emplace<QList<Location>>(locations);
238 } else if (value.isObject()) {
239 emplace<Location>(value.toObject());
240 } else {
241 emplace<std::nullptr_t>(nullptr);
242 }
243 }
244
245 template<typename Symbol>
documentSymbolsResultArray(const QJsonArray & array)246 QList<Symbol> documentSymbolsResultArray(const QJsonArray &array)
247 {
248 QList<Symbol> ret;
249 for (const auto &arrayValue : array) {
250 if (arrayValue.isObject())
251 ret << Symbol(arrayValue.toObject());
252 }
253 return ret;
254 }
255
DocumentSymbolsResult(const QJsonValue & value)256 DocumentSymbolsResult::DocumentSymbolsResult(const QJsonValue &value)
257 {
258 if (value.isArray()) {
259 QJsonArray array = value.toArray();
260 if (array.isEmpty()) {
261 *this = QList<SymbolInformation>();
262 } else {
263 QJsonObject arrayObject = array.first().toObject();
264 if (arrayObject.contains(rangeKey))
265 *this = documentSymbolsResultArray<DocumentSymbol>(array);
266 else
267 *this = documentSymbolsResultArray<SymbolInformation>(array);
268 }
269 } else {
270 *this = nullptr;
271 }
272 }
273
DocumentHighlightsResult(const QJsonValue & value)274 DocumentHighlightsResult::DocumentHighlightsResult(const QJsonValue &value)
275 {
276 if (value.isArray()) {
277 QList<DocumentHighlight> highlights;
278 for (auto arrayValue : value.toArray()) {
279 if (arrayValue.isObject())
280 highlights.append(DocumentHighlight(arrayValue.toObject()));
281 }
282 *this = highlights;
283 } else {
284 *this = nullptr;
285 }
286 }
287
MarkedString(const QJsonValue & value)288 MarkedString::MarkedString(const QJsonValue &value)
289 {
290 if (value.isObject())
291 emplace<MarkedLanguageString>(MarkedLanguageString(value.toObject()));
292 else
293 emplace<QString>(value.toString());
294 }
295
isValid() const296 bool MarkedString::isValid() const
297 {
298 if (auto markedLanguageString = Utils::get_if<MarkedLanguageString>(this))
299 return markedLanguageString->isValid();
300 return true;
301 }
302
operator QJsonValue() const303 LanguageServerProtocol::MarkedString::operator QJsonValue() const
304 {
305 if (auto val = Utils::get_if<QString>(this))
306 return *val;
307 if (auto val = Utils::get_if<MarkedLanguageString>(this))
308 return QJsonValue(*val);
309 return {};
310 }
311
HoverContent(const QJsonValue & value)312 HoverContent::HoverContent(const QJsonValue &value)
313 {
314 if (value.isArray()) {
315 emplace<QList<MarkedString>>(LanguageClientArray<MarkedString>(value).toList());
316 } else if (value.isObject()) {
317 const QJsonObject &object = value.toObject();
318 MarkedLanguageString markedLanguageString(object);
319 if (markedLanguageString.isValid())
320 emplace<MarkedString>(markedLanguageString);
321 else
322 emplace<MarkupContent>(MarkupContent(object));
323 } else if (value.isString()) {
324 emplace<MarkedString>(MarkedString(value.toString()));
325 }
326 }
327
isValid() const328 bool HoverContent::isValid() const
329 {
330 if (Utils::holds_alternative<MarkedString>(*this))
331 return Utils::get<MarkedString>(*this).isValid();
332 return true;
333 }
334
DocumentFormattingProperty(const QJsonValue & value)335 DocumentFormattingProperty::DocumentFormattingProperty(const QJsonValue &value)
336 {
337 if (value.isBool())
338 *this = value.toBool();
339 if (value.isDouble())
340 *this = value.toDouble();
341 if (value.isString())
342 *this = value.toString();
343 }
344
SignatureHelpRequest(const TextDocumentPositionParams & params)345 SignatureHelpRequest::SignatureHelpRequest(const TextDocumentPositionParams ¶ms)
346 : Request(methodName, params)
347 { }
348
CodeActionResult(const QJsonValue & val)349 CodeActionResult::CodeActionResult(const QJsonValue &val)
350 {
351 using ResultArray = QList<Utils::variant<Command, CodeAction>>;
352 if (val.isArray()) {
353 const QJsonArray array = val.toArray();
354 ResultArray result;
355 for (const QJsonValue &val : array) {
356 if (val.toObject().value(commandKey).isString()) {
357 const Command command(val);
358 if (command.isValid())
359 result << command;
360 } else {
361 const CodeAction action(val);
362 if (action.isValid())
363 result << action;
364 }
365 }
366 emplace<ResultArray>(result);
367 return;
368 }
369 emplace<std::nullptr_t>(nullptr);
370 }
371
tokens() const372 Utils::optional<QList<SemanticHighlightToken>> SemanticHighlightingInformation::tokens() const
373 {
374 QList<SemanticHighlightToken> resultTokens;
375
376 const QByteArray tokensByteArray = QByteArray::fromBase64(
377 typedValue<QString>(tokensKey).toLocal8Bit());
378 constexpr int tokensByteSize = 8;
379 int index = 0;
380 while (index + tokensByteSize <= tokensByteArray.size()) {
381 resultTokens << SemanticHighlightToken(tokensByteArray.mid(index, tokensByteSize));
382 index += tokensByteSize;
383 }
384 return Utils::make_optional(resultTokens);
385 }
386
setTokens(const QList<SemanticHighlightToken> & tokens)387 void SemanticHighlightingInformation::setTokens(const QList<SemanticHighlightToken> &tokens)
388 {
389 QByteArray byteArray;
390 byteArray.reserve(8 * tokens.size());
391 for (const SemanticHighlightToken &token : tokens)
392 token.appendToByteArray(byteArray);
393 insert(tokensKey, QString::fromLocal8Bit(byteArray.toBase64()));
394 }
395
SemanticHighlightToken(const QByteArray & token)396 SemanticHighlightToken::SemanticHighlightToken(const QByteArray &token)
397 {
398 QTC_ASSERT(token.size() == 8, return );
399 character = ( quint32(token.at(0)) << 24
400 | quint32(token.at(1)) << 16
401 | quint32(token.at(2)) << 8
402 | quint32(token.at(3)));
403
404 length = quint16(token.at(4) << 8 | token.at(5));
405
406 scope = quint16(token.at(6) << 8 | token.at(7));
407 }
408
appendToByteArray(QByteArray & byteArray) const409 void SemanticHighlightToken::appendToByteArray(QByteArray &byteArray) const
410 {
411 byteArray.append(char((character & 0xff000000) >> 24));
412 byteArray.append(char((character & 0x00ff0000) >> 16));
413 byteArray.append(char((character & 0x0000ff00) >> 8));
414 byteArray.append(char((character & 0x000000ff)));
415 byteArray.append(char((length & 0xff00) >> 8));
416 byteArray.append(char((length & 0x00ff)));
417 byteArray.append(char((scope & 0xff00) >> 8));
418 byteArray.append(char((scope & 0x00ff)));
419 }
420
421 Utils::variant<VersionedTextDocumentIdentifier, TextDocumentIdentifier>
textDocument() const422 SemanticHighlightingParams::textDocument() const
423 {
424 VersionedTextDocumentIdentifier textDocument = fromJsonValue<VersionedTextDocumentIdentifier>(
425 value(textDocumentKey));
426 return textDocument.isValid() ? textDocument : TextDocumentIdentifier(textDocument);
427 }
428
PrepareRenameResult()429 PrepareRenameResult::PrepareRenameResult()
430 : Utils::variant<PlaceHolderResult, Range, std::nullptr_t>(nullptr)
431 {}
432
PrepareRenameResult(const Utils::variant<PlaceHolderResult,Range,std::nullptr_t> & val)433 PrepareRenameResult::PrepareRenameResult(
434 const Utils::variant<PlaceHolderResult, Range, std::nullptr_t> &val)
435 : Utils::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
436 {}
437
PrepareRenameResult(const PlaceHolderResult & val)438 PrepareRenameResult::PrepareRenameResult(const PlaceHolderResult &val)
439 : Utils::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
440
441 {}
442
PrepareRenameResult(const Range & val)443 PrepareRenameResult::PrepareRenameResult(const Range &val)
444 : Utils::variant<PlaceHolderResult, Range, std::nullptr_t>(val)
445 {}
446
PrepareRenameResult(const QJsonValue & val)447 PrepareRenameResult::PrepareRenameResult(const QJsonValue &val)
448 {
449 if (val.isNull()) {
450 emplace<std::nullptr_t>(nullptr);
451 } else if (val.isObject()) {
452 const QJsonObject object = val.toObject();
453 if (object.keys().contains(rangeKey))
454 emplace<PlaceHolderResult>(PlaceHolderResult(object));
455 else
456 emplace<Range>(Range(object));
457 }
458 }
459
SemanticHighlightNotification(const SemanticHighlightingParams & params)460 SemanticHighlightNotification::SemanticHighlightNotification(const SemanticHighlightingParams ¶ms)
461 : Notification(methodName, params)
462 {}
463
data() const464 Utils::optional<QJsonValue> CodeLens::data() const
465 {
466 return contains(dataKey) ? Utils::make_optional(value(dataKey)) : Utils::nullopt;
467 }
468
469 } // namespace LanguageServerProtocol
470