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 &params)
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 &params)
84     : Request(methodName, params)
85 { }
86 
GotoTypeDefinitionRequest(const TextDocumentPositionParams & params)87 GotoTypeDefinitionRequest::GotoTypeDefinitionRequest(const TextDocumentPositionParams &params)
88     : Request(methodName, params)
89 { }
90 
GotoImplementationRequest(const TextDocumentPositionParams & params)91 GotoImplementationRequest::GotoImplementationRequest(const TextDocumentPositionParams &params)
92     : Request(methodName, params)
93 { }
94 
FindReferencesRequest(const ReferenceParams & params)95 FindReferencesRequest::FindReferencesRequest(const ReferenceParams &params)
96     : Request(methodName, params)
97 { }
98 
DocumentHighlightsRequest(const TextDocumentPositionParams & params)99 DocumentHighlightsRequest::DocumentHighlightsRequest(const TextDocumentPositionParams &params)
100     : Request(methodName, params)
101 { }
102 
DocumentSymbolsRequest(const DocumentSymbolParams & params)103 DocumentSymbolsRequest::DocumentSymbolsRequest(const DocumentSymbolParams &params)
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 &params)
118     : Request(methodName, params)
119 { }
120 
CodeLensRequest(const CodeLensParams & params)121 CodeLensRequest::CodeLensRequest(const CodeLensParams &params)
122     : Request(methodName, params)
123 { }
124 
CodeLensResolveRequest(const CodeLens & params)125 CodeLensResolveRequest::CodeLensResolveRequest(const CodeLens &params)
126     : Request(methodName, params)
127 { }
128 
DocumentLinkRequest(const DocumentLinkParams & params)129 DocumentLinkRequest::DocumentLinkRequest(const DocumentLinkParams &params)
130     : Request(methodName, params)
131 { }
132 
DocumentLinkResolveRequest(const DocumentLink & params)133 DocumentLinkResolveRequest::DocumentLinkResolveRequest(const DocumentLink &params)
134     : Request(methodName, params)
135 { }
136 
DocumentColorRequest(const DocumentColorParams & params)137 DocumentColorRequest::DocumentColorRequest(const DocumentColorParams &params)
138     : Request(methodName, params)
139 { }
140 
ColorPresentationRequest(const ColorPresentationParams & params)141 ColorPresentationRequest::ColorPresentationRequest(const ColorPresentationParams &params)
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 &params)
174     : Request(methodName, params)
175 { }
176 
DocumentRangeFormattingRequest(const DocumentRangeFormattingParams & params)177 DocumentRangeFormattingRequest::DocumentRangeFormattingRequest(
178         const DocumentRangeFormattingParams &params)
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 &params)
190     : Request(methodName, params)
191 { }
192 
PrepareRenameRequest(const TextDocumentPositionParams & params)193 PrepareRenameRequest::PrepareRenameRequest(const TextDocumentPositionParams &params)
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 &params)
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 &params)
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 &params)
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