LSP: fix hover request result

According to the protocol a hover request can also return a Null as a
result. Reflect this in the protocol implementation and adapt usages.

Change-Id: I14ce71639c64b6de00e9c1198617083c1a3de9eb
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
David Schulz
2022-05-16 13:31:10 +02:00
parent 2b8d96465a
commit 52770b746e
4 changed files with 71 additions and 36 deletions

View File

@@ -403,4 +403,19 @@ Utils::optional<QJsonValue> CodeLens::data() const
return contains(dataKey) ? Utils::make_optional(value(dataKey)) : Utils::nullopt; return contains(dataKey) ? Utils::make_optional(value(dataKey)) : Utils::nullopt;
} }
HoverResult::HoverResult(const QJsonValue &value)
{
if (value.isObject())
emplace<Hover>(Hover(value.toObject()));
else
emplace<std::nullptr_t>(nullptr);
}
bool HoverResult::isValid() const
{
if (auto hover = Utils::get_if<Hover>(this))
return hover->isValid();
return true;
}
} // namespace LanguageServerProtocol } // namespace LanguageServerProtocol

View File

@@ -101,8 +101,17 @@ public:
bool isValid() const override { return contains(contentsKey); } bool isValid() const override { return contains(contentsKey); }
}; };
class LANGUAGESERVERPROTOCOL_EXPORT HoverResult : public Utils::variant<Hover, std::nullptr_t>
{
public:
HoverResult() : variant(nullptr) {}
explicit HoverResult(const Hover &hover) : variant(hover) {}
explicit HoverResult(const QJsonValue &value);
bool isValid() const;
};
class LANGUAGESERVERPROTOCOL_EXPORT HoverRequest class LANGUAGESERVERPROTOCOL_EXPORT HoverRequest
: public Request<Hover, std::nullptr_t, TextDocumentPositionParams> : public Request<HoverResult, std::nullptr_t, TextDocumentPositionParams>
{ {
public: public:
explicit HoverRequest(const TextDocumentPositionParams &params); explicit HoverRequest(const TextDocumentPositionParams &params);

View File

@@ -2344,35 +2344,38 @@ void ClangdClient::findLocalUsages(TextDocument *document, const QTextCursor &cu
void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverResponse, void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverResponse,
const DocumentUri &uri) const DocumentUri &uri)
{ {
if (const Utils::optional<Hover> result = hoverResponse.result()) { if (const Utils::optional<HoverResult> result = hoverResponse.result()) {
const HoverContent content = result->content(); if (auto hover = Utils::get_if<Hover>(&(*result))) {
const MarkupContent * const markup = Utils::get_if<MarkupContent>(&content); const HoverContent content = hover->content();
if (markup) { const MarkupContent *const markup = Utils::get_if<MarkupContent>(&content);
const QString markupString = markup->content(); if (markup) {
const QString markupString = markup->content();
// Macros aren't locatable via the AST, so parse the formatted string. // Macros aren't locatable via the AST, so parse the formatted string.
static const QString magicMacroPrefix = "### macro `"; static const QString magicMacroPrefix = "### macro `";
if (markupString.startsWith(magicMacroPrefix)) { if (markupString.startsWith(magicMacroPrefix)) {
const int nameStart = magicMacroPrefix.length(); const int nameStart = magicMacroPrefix.length();
const int closingQuoteIndex = markupString.indexOf('`', nameStart); const int closingQuoteIndex = markupString.indexOf('`', nameStart);
if (closingQuoteIndex != -1) { if (closingQuoteIndex != -1) {
const QString macroName = markupString.mid(nameStart, const QString macroName = markupString.mid(nameStart,
closingQuoteIndex - nameStart); closingQuoteIndex - nameStart);
d->setHelpItemForTooltip(hoverResponse.id(), macroName, HelpItem::Macro); d->setHelpItemForTooltip(hoverResponse.id(), macroName, HelpItem::Macro);
return; return;
}
} }
}
// Is it the file path for an include directive? // Is it the file path for an include directive?
QString cleanString = markupString; QString cleanString = markupString;
cleanString.remove('`'); cleanString.remove('`');
const QStringList lines = cleanString.trimmed().split('\n'); const QStringList lines = cleanString.trimmed().split('\n');
if (!lines.isEmpty()) { if (!lines.isEmpty()) {
const auto filePath = Utils::FilePath::fromUserInput(lines.last().simplified()); const auto filePath = Utils::FilePath::fromUserInput(lines.last().simplified());
if (filePath.exists()) { if (filePath.exists()) {
d->setHelpItemForTooltip(hoverResponse.id(), filePath.fileName(), d->setHelpItemForTooltip(hoverResponse.id(),
HelpItem::Brief); filePath.fileName(),
return; HelpItem::Brief);
return;
}
} }
} }
} }
@@ -2382,7 +2385,11 @@ void ClangdClient::gatherHelpItemForTooltip(const HoverRequest::Response &hoverR
QTC_ASSERT(doc, return); QTC_ASSERT(doc, return);
const auto astHandler = [this, uri, hoverResponse](const AstNode &ast, const MessageId &) { const auto astHandler = [this, uri, hoverResponse](const AstNode &ast, const MessageId &) {
const MessageId id = hoverResponse.id(); const MessageId id = hoverResponse.id();
const Range range = hoverResponse.result()->range().value_or(Range()); Range range;
if (const Utils::optional<HoverResult> result = hoverResponse.result()) {
if (auto hover = Utils::get_if<Hover>(&(*result)))
range = hover->range().value_or(Range());
}
const QList<AstNode> path = getAstPath(ast, range); const QList<AstNode> path = getAstPath(ast, range);
if (path.isEmpty()) { if (path.isEmpty()) {
d->setHelpItemForTooltip(id); d->setHelpItemForTooltip(id);

View File

@@ -58,8 +58,10 @@ void HoverHandler::setHelpItem(const LanguageServerProtocol::MessageId &msgId,
const Core::HelpItem &help) const Core::HelpItem &help)
{ {
if (msgId == m_response.id()) { if (msgId == m_response.id()) {
if (Utils::optional<Hover> result = m_response.result()) if (Utils::optional<HoverResult> result = m_response.result()) {
setContent(result->content()); if (auto hover = Utils::get_if<Hover>(&(*result)))
setContent(hover->content());
}
m_response = {}; m_response = {};
setLastHelpItemIdentified(help); setLastHelpItemIdentified(help);
m_report(priority()); m_report(priority());
@@ -129,13 +131,15 @@ void HoverHandler::handleResponse(const HoverRequest::Response &response)
if (m_client) if (m_client)
m_client->log(*error); m_client->log(*error);
} }
if (Utils::optional<Hover> result = response.result()) { if (Utils::optional<HoverResult> result = response.result()) {
if (m_helpItemProvider) { if (auto hover = Utils::get_if<Hover>(&(*result))) {
m_response = response; if (m_helpItemProvider) {
m_helpItemProvider(response, m_uri); m_response = response;
return; m_helpItemProvider(response, m_uri);
return;
}
setContent(hover->content());
} }
setContent(result->content());
} }
m_report(priority()); m_report(priority());
} }