Clang: Provide tooltips

This includes also the query data for the help system (F1) for an
identifier under cursor.

Regressions (libclang changes necessary):
 - Function signatures do not contain default values.
 - Aliases are not resolved for/at:
   - template types
   - qualified name of a type

Fixes/Improvements:
 - Resolve "auto"
 - On a template type, show also the template parameter.
 - For a typedef like
     typedef long long superlong;
   the tooltip was "long long superlong", which was confusing.
   Now, "long long" is shown.

New:
 - Show first or \brief paragraph of a documentation comment.
 - Show size of a class at definition.
 - Show size of a field member in class definition.

Task-number: QTCREATORBUG-11259
Change-Id: Ie1a07930d0e882015d07dc43e35bb81a685cdeb8
Reviewed-by: Marco Bubke <marco.bubke@qt.io>
This commit is contained in:
Nikolai Kosjar
2018-01-12 12:29:43 +01:00
parent 7872ddde4c
commit 76c25bcd6a
71 changed files with 2824 additions and 112 deletions

View File

@@ -53,6 +53,9 @@ void ClangCodeModelClientInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::FollowSymbolMessage: case MessageType::FollowSymbolMessage:
followSymbol(messageEnvelop.message<FollowSymbolMessage>()); followSymbol(messageEnvelop.message<FollowSymbolMessage>());
break; break;
case MessageType::ToolTipMessage:
tooltip(messageEnvelop.message<ToolTipMessage>());
break;
default: default:
qWarning() << "Unknown ClangCodeModelClientMessage"; qWarning() << "Unknown ClangCodeModelClientMessage";
} }

View File

@@ -43,6 +43,8 @@ class RegisterUnsavedFilesForEditorMessage;
class RequestDocumentAnnotationsMessage; class RequestDocumentAnnotationsMessage;
class RequestReferencesMessage; class RequestReferencesMessage;
class RequestFollowSymbolMessage; class RequestFollowSymbolMessage;
class RequestToolTipMessage;
class ToolTipMessage;
class UnregisterProjectPartsForEditorMessage; class UnregisterProjectPartsForEditorMessage;
class UnregisterTranslationUnitsForEditorMessage; class UnregisterTranslationUnitsForEditorMessage;
class UnregisterUnsavedFilesForEditorMessage; class UnregisterUnsavedFilesForEditorMessage;
@@ -60,6 +62,7 @@ public:
virtual void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) = 0; virtual void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) = 0;
virtual void references(const ReferencesMessage &message) = 0; virtual void references(const ReferencesMessage &message) = 0;
virtual void followSymbol(const FollowSymbolMessage &message) = 0; virtual void followSymbol(const FollowSymbolMessage &message) = 0;
virtual void tooltip(const ToolTipMessage &message) = 0;
}; };
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -31,3 +31,4 @@
#include "documentannotationschangedmessage.h" #include "documentannotationschangedmessage.h"
#include "referencesmessage.h" #include "referencesmessage.h"
#include "followsymbolmessage.h" #include "followsymbolmessage.h"
#include "tooltipmessage.h"

View File

@@ -95,6 +95,11 @@ void ClangCodeModelClientProxy::followSymbol(const FollowSymbolMessage &message)
m_writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void ClangCodeModelClientProxy::tooltip(const ToolTipMessage &message)
{
m_writeMessageBlock.write(message);
}
void ClangCodeModelClientProxy::readMessages() void ClangCodeModelClientProxy::readMessages()
{ {
for (const MessageEnvelop &message : m_readMessageBlock.readAll()) for (const MessageEnvelop &message : m_readMessageBlock.readAll())

View File

@@ -57,6 +57,7 @@ public:
void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) override; void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) override;
void references(const ReferencesMessage &message) override; void references(const ReferencesMessage &message) override;
void followSymbol(const FollowSymbolMessage &message) override; void followSymbol(const FollowSymbolMessage &message) override;
void tooltip(const ToolTipMessage &message) override;
void readMessages(); void readMessages();

View File

@@ -71,6 +71,9 @@ void ClangCodeModelServerInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::RequestFollowSymbolMessage: case MessageType::RequestFollowSymbolMessage:
requestFollowSymbol(messageEnvelop.message<RequestFollowSymbolMessage>()); requestFollowSymbol(messageEnvelop.message<RequestFollowSymbolMessage>());
break; break;
case MessageType::RequestToolTipMessage:
requestToolTip(messageEnvelop.message<RequestToolTipMessage>());
break;
case MessageType::UpdateVisibleTranslationUnitsMessage: case MessageType::UpdateVisibleTranslationUnitsMessage:
updateVisibleTranslationUnits(messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>()); updateVisibleTranslationUnits(messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>());
break; break;

View File

@@ -50,6 +50,7 @@ public:
virtual void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) = 0; virtual void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) = 0;
virtual void requestReferences(const RequestReferencesMessage &message) = 0; virtual void requestReferences(const RequestReferencesMessage &message) = 0;
virtual void requestFollowSymbol(const RequestFollowSymbolMessage &message) = 0; virtual void requestFollowSymbol(const RequestFollowSymbolMessage &message) = 0;
virtual void requestToolTip(const RequestToolTipMessage &message) = 0;
virtual void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) = 0; virtual void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) = 0;
}; };

View File

@@ -36,6 +36,7 @@
#include "requestdocumentannotations.h" #include "requestdocumentannotations.h"
#include "requestreferencesmessage.h" #include "requestreferencesmessage.h"
#include "requestfollowsymbolmessage.h" #include "requestfollowsymbolmessage.h"
#include "requesttooltipmessage.h"
#include "unregisterunsavedfilesforeditormessage.h" #include "unregisterunsavedfilesforeditormessage.h"
#include "updatetranslationunitsforeditormessage.h" #include "updatetranslationunitsforeditormessage.h"
#include "updatevisibletranslationunitsmessage.h" #include "updatevisibletranslationunitsmessage.h"

View File

@@ -96,6 +96,11 @@ void ClangCodeModelServerProxy::requestFollowSymbol(const RequestFollowSymbolMes
m_writeMessageBlock.write(message); m_writeMessageBlock.write(message);
} }
void ClangCodeModelServerProxy::requestToolTip(const RequestToolTipMessage &message)
{
m_writeMessageBlock.write(message);
}
void ClangCodeModelServerProxy::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) void ClangCodeModelServerProxy::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{ {
m_writeMessageBlock.write(message); m_writeMessageBlock.write(message);

View File

@@ -60,6 +60,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override; void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override; void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
void requestToolTip(const RequestToolTipMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
}; };

View File

@@ -68,6 +68,7 @@ SOURCES += \
$$PWD/requestsourcelocationforrenamingmessage.cpp \ $$PWD/requestsourcelocationforrenamingmessage.cpp \
$$PWD/requestsourcerangesanddiagnosticsforquerymessage.cpp \ $$PWD/requestsourcerangesanddiagnosticsforquerymessage.cpp \
$$PWD/requestsourcerangesforquerymessage.cpp \ $$PWD/requestsourcerangesforquerymessage.cpp \
$$PWD/requesttooltipmessage.cpp \
$$PWD/sourcelocationcontainer.cpp \ $$PWD/sourcelocationcontainer.cpp \
$$PWD/sourcelocationcontainerv2.cpp \ $$PWD/sourcelocationcontainerv2.cpp \
$$PWD/sourcelocationscontainer.cpp \ $$PWD/sourcelocationscontainer.cpp \
@@ -82,6 +83,8 @@ SOURCES += \
$$PWD/sourcerangesforquerymessage.cpp \ $$PWD/sourcerangesforquerymessage.cpp \
$$PWD/sourcerangewithtextcontainer.cpp \ $$PWD/sourcerangewithtextcontainer.cpp \
$$PWD/tokeninfocontainer.cpp \ $$PWD/tokeninfocontainer.cpp \
$$PWD/tooltipmessage.cpp \
$$PWD/tooltipinfo.cpp \
$$PWD/unregisterunsavedfilesforeditormessage.cpp \ $$PWD/unregisterunsavedfilesforeditormessage.cpp \
$$PWD/updatepchprojectpartsmessage.cpp \ $$PWD/updatepchprojectpartsmessage.cpp \
$$PWD/updatetranslationunitsforeditormessage.cpp \ $$PWD/updatetranslationunitsforeditormessage.cpp \
@@ -159,6 +162,7 @@ HEADERS += \
$$PWD/requestsourcelocationforrenamingmessage.h \ $$PWD/requestsourcelocationforrenamingmessage.h \
$$PWD/requestsourcerangesanddiagnosticsforquerymessage.h \ $$PWD/requestsourcerangesanddiagnosticsforquerymessage.h \
$$PWD/requestsourcerangesforquerymessage.h \ $$PWD/requestsourcerangesforquerymessage.h \
$$PWD/requesttooltipmessage.h \
$$PWD/sourcelocationcontainer.h \ $$PWD/sourcelocationcontainer.h \
$$PWD/sourcelocationcontainerv2.h \ $$PWD/sourcelocationcontainerv2.h \
$$PWD/sourcelocationscontainer.h \ $$PWD/sourcelocationscontainer.h \
@@ -176,6 +180,8 @@ HEADERS += \
$$PWD/sourcerangewithtextcontainer.h \ $$PWD/sourcerangewithtextcontainer.h \
$$PWD/stringcache.h \ $$PWD/stringcache.h \
$$PWD/tokeninfocontainer.h \ $$PWD/tokeninfocontainer.h \
$$PWD/tooltipmessage.h \
$$PWD/tooltipinfo.h \
$$PWD/unregisterunsavedfilesforeditormessage.h \ $$PWD/unregisterunsavedfilesforeditormessage.h \
$$PWD/updatepchprojectpartsmessage.h \ $$PWD/updatepchprojectpartsmessage.h \
$$PWD/updatetranslationunitsforeditormessage.h \ $$PWD/updatetranslationunitsforeditormessage.h \

View File

@@ -120,6 +120,9 @@ enum class MessageType : quint8 {
RequestFollowSymbolMessage, RequestFollowSymbolMessage,
FollowSymbolMessage, FollowSymbolMessage,
RequestToolTipMessage,
ToolTipMessage,
UpdateVisibleTranslationUnitsMessage, UpdateVisibleTranslationUnitsMessage,
CompleteCodeMessage, CompleteCodeMessage,

View File

@@ -37,7 +37,8 @@ QDebug operator<<(QDebug debug, const FileContainer &container)
<< container.filePath() << ", " << container.filePath() << ", "
<< container.projectPartId() << ", " << container.projectPartId() << ", "
<< container.fileArguments() << ", " << container.fileArguments() << ", "
<< container.documentRevision(); << container.documentRevision() << ", "
<< container.textCodecName();
if (container.hasUnsavedFileContent()) { if (container.hasUnsavedFileContent()) {
const Utf8String fileWithContent = debugWriteFileForInspection( const Utf8String fileWithContent = debugWriteFileForInspection(

View File

@@ -42,10 +42,12 @@ public:
const Utf8String &projectPartId, const Utf8String &projectPartId,
const Utf8String &unsavedFileContent = Utf8String(), const Utf8String &unsavedFileContent = Utf8String(),
bool hasUnsavedFileContent = false, bool hasUnsavedFileContent = false,
quint32 documentRevision = 0) quint32 documentRevision = 0,
const Utf8String &textCodecName = Utf8String())
: m_filePath(filePath), : m_filePath(filePath),
m_projectPartId(projectPartId), m_projectPartId(projectPartId),
m_unsavedFileContent(unsavedFileContent), m_unsavedFileContent(unsavedFileContent),
m_textCodecName(textCodecName),
m_documentRevision(documentRevision), m_documentRevision(documentRevision),
m_hasUnsavedFileContent(hasUnsavedFileContent) m_hasUnsavedFileContent(hasUnsavedFileContent)
{ {
@@ -98,6 +100,11 @@ public:
return m_unsavedFileContent; return m_unsavedFileContent;
} }
const Utf8String &textCodecName() const
{
return m_textCodecName;
}
bool hasUnsavedFileContent() const bool hasUnsavedFileContent() const
{ {
return m_hasUnsavedFileContent; return m_hasUnsavedFileContent;
@@ -114,6 +121,7 @@ public:
out << container.m_projectPartId; out << container.m_projectPartId;
out << container.m_fileArguments; out << container.m_fileArguments;
out << container.m_unsavedFileContent; out << container.m_unsavedFileContent;
out << container.m_textCodecName;
out << container.m_documentRevision; out << container.m_documentRevision;
out << container.m_hasUnsavedFileContent; out << container.m_hasUnsavedFileContent;
@@ -126,6 +134,7 @@ public:
in >> container.m_projectPartId; in >> container.m_projectPartId;
in >> container.m_fileArguments; in >> container.m_fileArguments;
in >> container.m_unsavedFileContent; in >> container.m_unsavedFileContent;
in >> container.m_textCodecName;
in >> container.m_documentRevision; in >> container.m_documentRevision;
in >> container.m_hasUnsavedFileContent; in >> container.m_hasUnsavedFileContent;
@@ -142,6 +151,7 @@ private:
Utf8String m_projectPartId; Utf8String m_projectPartId;
Utf8StringVector m_fileArguments; Utf8StringVector m_fileArguments;
Utf8String m_unsavedFileContent; Utf8String m_unsavedFileContent;
Utf8String m_textCodecName;
quint32 m_documentRevision = 0; quint32 m_documentRevision = 0;
bool m_hasUnsavedFileContent = false; bool m_hasUnsavedFileContent = false;
}; };

View File

@@ -68,6 +68,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::RequestReferencesMessage: case MessageType::RequestReferencesMessage:
qDebug() << messageEnvelop.message<RequestReferencesMessage>(); qDebug() << messageEnvelop.message<RequestReferencesMessage>();
break; break;
case MessageType::RequestToolTipMessage:
qDebug() << messageEnvelop.message<RequestToolTipMessage>();
break;
case MessageType::UpdateVisibleTranslationUnitsMessage: case MessageType::UpdateVisibleTranslationUnitsMessage:
qDebug() << messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>(); qDebug() << messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>();
break; break;
@@ -83,6 +86,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::ReferencesMessage: case MessageType::ReferencesMessage:
qDebug() << messageEnvelop.message<ReferencesMessage>(); qDebug() << messageEnvelop.message<ReferencesMessage>();
break; break;
case MessageType::ToolTipMessage:
qDebug() << messageEnvelop.message<ToolTipMessage>();
break;
case MessageType::DocumentAnnotationsChangedMessage: case MessageType::DocumentAnnotationsChangedMessage:
qDebug() << messageEnvelop.message<DocumentAnnotationsChangedMessage>(); qDebug() << messageEnvelop.message<DocumentAnnotationsChangedMessage>();
break; break;

View File

@@ -0,0 +1,48 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "requesttooltipmessage.h"
#include <QDebug>
namespace ClangBackEnd {
quint64 RequestToolTipMessage::ticketCounter = 0;
QDebug operator<<(QDebug debug, const RequestToolTipMessage &message)
{
debug.nospace() << "RequestToolTipMessage(";
debug.nospace() << message.m_fileContainer << ", ";
debug.nospace() << message.m_ticketNumber << ", ";
debug.nospace() << message.m_line << ", ";
debug.nospace() << message.m_column << ", ";
debug.nospace() << ")";
return debug;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,94 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "clangsupport_global.h"
#include "filecontainer.h"
#include <QDataStream>
namespace ClangBackEnd {
// TODO: De-duplicate with RequestReferencesMessage?
class RequestToolTipMessage
{
public:
RequestToolTipMessage() = default;
RequestToolTipMessage(const FileContainer &fileContainer, quint32 line, quint32 column)
: m_fileContainer(fileContainer)
, m_ticketNumber(++ticketCounter)
, m_line(line)
, m_column(column)
{
}
const FileContainer fileContainer() const { return m_fileContainer; }
quint32 line() const { return m_line; }
quint32 column() const { return m_column; }
quint64 ticketNumber() const { return m_ticketNumber; }
friend QDataStream &operator<<(QDataStream &out, const RequestToolTipMessage &message)
{
out << message.m_fileContainer;
out << message.m_ticketNumber;
out << message.m_line;
out << message.m_column;
return out;
}
friend QDataStream &operator>>(QDataStream &in, RequestToolTipMessage &message)
{
in >> message.m_fileContainer;
in >> message.m_ticketNumber;
in >> message.m_line;
in >> message.m_column;
return in;
}
friend bool operator==(const RequestToolTipMessage &first,
const RequestToolTipMessage &second)
{
return first.m_ticketNumber == second.m_ticketNumber
&& first.m_line == second.m_line
&& first.m_column == second.m_column
&& first.m_fileContainer == second.m_fileContainer;
}
friend CLANGSUPPORT_EXPORT QDebug operator<<(QDebug debug, const RequestToolTipMessage &message);
private:
FileContainer m_fileContainer;
quint64 m_ticketNumber = 0;
quint32 m_line = 0;
quint32 m_column = 0;
static CLANGSUPPORT_EXPORT quint64 ticketCounter;
};
DECLARE_MESSAGE(RequestToolTipMessage);
} // namespace ClangBackEnd

View File

@@ -0,0 +1,65 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "tooltipinfo.h"
#include <QDebug>
namespace ClangBackEnd {
#define RETURN_TEXT_FOR_CASE(enumValue) case ToolTipInfo::enumValue: return #enumValue
const char *qdocCategoryToString(ToolTipInfo::QdocCategory category)
{
switch (category) {
RETURN_TEXT_FOR_CASE(Unknown);
RETURN_TEXT_FOR_CASE(ClassOrNamespace);
RETURN_TEXT_FOR_CASE(Enum);
RETURN_TEXT_FOR_CASE(Typedef);
RETURN_TEXT_FOR_CASE(Macro);
RETURN_TEXT_FOR_CASE(Brief);
RETURN_TEXT_FOR_CASE(Function);
}
return "UnhandledQdocCategory";
}
#undef RETURN_TEXT_FOR_CASE
QDebug operator<<(QDebug debug, const ToolTipInfo &message)
{
debug.nospace() << "ToolTipInfo(";
debug.nospace() << message.m_text << ", ";
debug.nospace() << message.m_briefComment << ", ";
debug.nospace() << message.m_qdocIdCandidates << ", ";
debug.nospace() << message.m_qdocMark << ", ";
debug.nospace() << qdocCategoryToString(message.m_qdocCategory) << ", ";
debug.nospace() << message.m_sizeInBytes << ", ";
debug.nospace() << ")";
return debug;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utf8string.h>
#include <utf8stringvector.h>
namespace ClangBackEnd {
class ToolTipInfo
{
public:
enum QdocCategory : quint8 {
Unknown,
ClassOrNamespace,
Enum,
Typedef,
Macro,
Brief,
Function,
};
public:
ToolTipInfo() = default;
ToolTipInfo(const Utf8String &text) : m_text(text) {}
const Utf8String &text() const { return m_text; }
void setText(const Utf8String &text) { m_text = text; }
const Utf8String &briefComment() const { return m_briefComment; }
void setBriefComment(const Utf8String &briefComment) { m_briefComment = briefComment; }
const Utf8StringVector &qdocIdCandidates() const { return m_qdocIdCandidates; }
void setQdocIdCandidates(const Utf8StringVector &qdocIdCandidates)
{ m_qdocIdCandidates = qdocIdCandidates; }
const Utf8String &qdocMark() const { return m_qdocMark; }
void setQdocMark(const Utf8String &qdocMark) { m_qdocMark = qdocMark; }
const QdocCategory &qdocCategory() const { return m_qdocCategory; }
void setQdocCategory(const QdocCategory &qdocCategory) { m_qdocCategory = qdocCategory; }
const Utf8String &sizeInBytes() const { return m_sizeInBytes; }
void setSizeInBytes(const Utf8String &sizeInBytes) { m_sizeInBytes = sizeInBytes; }
friend QDataStream &operator<<(QDataStream &out, const ToolTipInfo &message)
{
out << message.m_text;
out << message.m_briefComment;
out << message.m_qdocIdCandidates;
out << message.m_qdocMark;
out << static_cast<quint8>(message.m_qdocCategory);
out << message.m_sizeInBytes;
return out;
}
friend QDataStream &operator>>(QDataStream &in, ToolTipInfo &message)
{
quint8 qdocCategory;
in >> message.m_text;
in >> message.m_briefComment;
in >> message.m_qdocIdCandidates;
in >> message.m_qdocMark;
in >> qdocCategory;
in >> message.m_sizeInBytes;
message.m_qdocCategory = static_cast<QdocCategory>(qdocCategory);
return in;
}
friend bool operator==(const ToolTipInfo &first, const ToolTipInfo &second)
{
return first.m_text == second.m_text
&& first.m_briefComment == second.m_briefComment
&& first.m_qdocIdCandidates == second.m_qdocIdCandidates
&& first.m_qdocMark == second.m_qdocMark
&& first.m_qdocCategory == second.m_qdocCategory
&& first.m_sizeInBytes == second.m_sizeInBytes;
}
friend QDebug operator<<(QDebug debug, const ToolTipInfo &message);
friend std::ostream &operator<<(std::ostream &os, const ToolTipInfo &message);
private:
Utf8String m_text;
Utf8String m_briefComment;
Utf8StringVector m_qdocIdCandidates;
Utf8String m_qdocMark;
QdocCategory m_qdocCategory = Unknown;
// For class definition and for class fields.
Utf8String m_sizeInBytes;
};
const char *qdocCategoryToString(ToolTipInfo::QdocCategory category);
} // namespace ClangBackEnd

View File

@@ -0,0 +1,44 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "tooltipmessage.h"
#include <QDebug>
namespace ClangBackEnd {
QDebug operator<<(QDebug debug, const ToolTipMessage &message)
{
debug.nospace() << "ToolTipMessage("
<< message.fileContainer()
<< ", " << message.m_ticketNumber
<< ", " << message.m_toolTipInfo;
debug.nospace() << ")";
return debug;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,87 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include "filecontainer.h"
#include "tooltipinfo.h"
#include <QDataStream>
namespace ClangBackEnd {
class ToolTipMessage
{
public:
ToolTipMessage() = default;
ToolTipMessage(const FileContainer &fileContainer,
const ToolTipInfo &toolTipInfo,
quint64 ticketNumber)
: m_fileContainer(fileContainer)
, m_toolTipInfo(toolTipInfo)
, m_ticketNumber(ticketNumber)
{
}
const FileContainer &fileContainer() const { return m_fileContainer; }
const ToolTipInfo &toolTipInfo() const { return m_toolTipInfo; }
quint64 ticketNumber() const { return m_ticketNumber; }
friend QDataStream &operator<<(QDataStream &out, const ToolTipMessage &message)
{
out << message.m_fileContainer;
out << message.m_toolTipInfo;;
out << message.m_ticketNumber;
return out;
}
friend QDataStream &operator>>(QDataStream &in, ToolTipMessage &message)
{
in >> message.m_fileContainer;
in >> message.m_toolTipInfo;
in >> message.m_ticketNumber;
return in;
}
friend bool operator==(const ToolTipMessage &first, const ToolTipMessage &second)
{
return first.m_ticketNumber == second.m_ticketNumber
&& first.m_fileContainer == second.m_fileContainer
&& first.m_toolTipInfo == second.m_toolTipInfo;
}
friend CLANGSUPPORT_EXPORT QDebug operator<<(QDebug debug, const ToolTipMessage &message);
friend std::ostream &operator<<(std::ostream &os, const ToolTipMessage &message);
private:
FileContainer m_fileContainer;
ToolTipInfo m_toolTipInfo;
quint64 m_ticketNumber = 0;
};
DECLARE_MESSAGE(ToolTipMessage)
} // namespace ClangBackEnd

View File

@@ -171,7 +171,8 @@ public:
byteArray.reserve(reserveSize); byteArray.reserve(reserveSize);
} }
static Utf8String number(int number, int base=10) template<typename T>
static Utf8String number(T number, int base = 10)
{ {
return Utf8String::fromByteArray(QByteArray::number(number, base)); return Utf8String::fromByteArray(QByteArray::number(number, base));
} }

View File

@@ -392,6 +392,15 @@ QFuture<CppTools::CursorInfo> BackendCommunicator::requestLocalReferences(
return m_receiver.addExpectedReferencesMessage(message.ticketNumber(), textDocument); return m_receiver.addExpectedReferencesMessage(message.ticketNumber(), textDocument);
} }
QFuture<CppTools::ToolTipInfo> BackendCommunicator::requestToolTip(
const FileContainer &fileContainer, quint32 line, quint32 column)
{
const RequestToolTipMessage message(fileContainer, line, column);
m_sender->requestToolTip(message);
return m_receiver.addExpectedToolTipMessage(message.ticketNumber());
}
QFuture<CppTools::SymbolInfo> BackendCommunicator::requestFollowSymbol( QFuture<CppTools::SymbolInfo> BackendCommunicator::requestFollowSymbol(
const FileContainer &curFileContainer, const FileContainer &curFileContainer,
quint32 line, quint32 line,

View File

@@ -82,6 +82,9 @@ public:
quint32 line, quint32 line,
quint32 column, quint32 column,
QTextDocument *textDocument); QTextDocument *textDocument);
QFuture<CppTools::ToolTipInfo> requestToolTip(const FileContainer &fileContainer,
quint32 line,
quint32 column);
QFuture<CppTools::SymbolInfo> requestFollowSymbol(const FileContainer &curFileContainer, QFuture<CppTools::SymbolInfo> requestFollowSymbol(const FileContainer &curFileContainer,
quint32 line, quint32 line,
quint32 column); quint32 column);

View File

@@ -128,6 +128,18 @@ QFuture<CppTools::SymbolInfo> BackendReceiver::addExpectedRequestFollowSymbolMes
return futureInterface.future(); return futureInterface.future();
} }
QFuture<CppTools::ToolTipInfo> BackendReceiver::addExpectedToolTipMessage(quint64 ticket)
{
QTC_CHECK(!m_toolTipsTable.contains(ticket));
QFutureInterface<CppTools::ToolTipInfo> futureInterface;
futureInterface.reportStarted();
m_toolTipsTable.insert(ticket, futureInterface);
return futureInterface.future();
}
bool BackendReceiver::isExpectingCodeCompletedMessage() const bool BackendReceiver::isExpectingCodeCompletedMessage() const
{ {
return !m_assistProcessorsTable.isEmpty(); return !m_assistProcessorsTable.isEmpty();
@@ -272,6 +284,72 @@ void BackendReceiver::references(const ReferencesMessage &message)
futureInterface.reportFinished(); futureInterface.reportFinished();
} }
static TextEditor::HelpItem::Category toHelpItemCategory(ToolTipInfo::QdocCategory category)
{
switch (category) {
case ToolTipInfo::Unknown:
return TextEditor::HelpItem::Unknown;
case ToolTipInfo::ClassOrNamespace:
return TextEditor::HelpItem::ClassOrNamespace;
case ToolTipInfo::Enum:
return TextEditor::HelpItem::Enum;
case ToolTipInfo::Typedef:
return TextEditor::HelpItem::Typedef;
case ToolTipInfo::Macro:
return TextEditor::HelpItem::Macro;
case ToolTipInfo::Brief:
return TextEditor::HelpItem::Brief;
case ToolTipInfo::Function:
return TextEditor::HelpItem::Function;
}
return TextEditor::HelpItem::Unknown;
}
static QStringList toStringList(const Utf8StringVector &utf8StringVector)
{
QStringList list;
list.reserve(utf8StringVector.size());
for (const Utf8String &utf8String : utf8StringVector)
list << utf8String.toString();
return list;
}
static CppTools::ToolTipInfo toToolTipInfo(const ToolTipMessage &message)
{
CppTools::ToolTipInfo info;
const ToolTipInfo backendInfo = message.toolTipInfo();
info.text = backendInfo.text();
info.briefComment = backendInfo.briefComment();
info.qDocIdCandidates = toStringList(backendInfo.qdocIdCandidates());
info.qDocMark = backendInfo.qdocMark();
info.qDocCategory = toHelpItemCategory(backendInfo.qdocCategory());
info.sizeInBytes = backendInfo.sizeInBytes();
return info;
}
void BackendReceiver::tooltip(const ToolTipMessage &message)
{
qCDebugIpc() << "ToolTipMessage" << message.toolTipInfo().text();
const quint64 ticket = message.ticketNumber();
QFutureInterface<CppTools::ToolTipInfo> futureInterface = m_toolTipsTable.take(ticket);
QTC_CHECK(futureInterface != QFutureInterface<CppTools::ToolTipInfo>());
if (futureInterface.isCanceled())
return; // A new request was issued making this one outdated.
futureInterface.reportResult(toToolTipInfo(message));
futureInterface.reportFinished();
}
void BackendReceiver::followSymbol(const ClangBackEnd::FollowSymbolMessage &message) void BackendReceiver::followSymbol(const ClangBackEnd::FollowSymbolMessage &message)
{ {
qCDebugIpc() << "FollowSymbolMessage with" qCDebugIpc() << "FollowSymbolMessage with"

View File

@@ -27,6 +27,7 @@
#include <cpptools/cppcursorinfo.h> #include <cpptools/cppcursorinfo.h>
#include <cpptools/cppsymbolinfo.h> #include <cpptools/cppsymbolinfo.h>
#include <cpptools/baseeditordocumentprocessor.h>
#include <clangsupport/clangcodemodelclientinterface.h> #include <clangsupport/clangcodemodelclientinterface.h>
@@ -59,6 +60,7 @@ public:
const CppTools::SemanticInfo::LocalUseMap &localUses const CppTools::SemanticInfo::LocalUseMap &localUses
= CppTools::SemanticInfo::LocalUseMap()); = CppTools::SemanticInfo::LocalUseMap());
QFuture<CppTools::SymbolInfo> addExpectedRequestFollowSymbolMessage(quint64 ticket); QFuture<CppTools::SymbolInfo> addExpectedRequestFollowSymbolMessage(quint64 ticket);
QFuture<CppTools::ToolTipInfo> addExpectedToolTipMessage(quint64 ticket);
bool isExpectingCodeCompletedMessage() const; bool isExpectingCodeCompletedMessage() const;
void reset(); void reset();
@@ -70,6 +72,7 @@ private:
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &message) override; void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &message) override;
void references(const ClangBackEnd::ReferencesMessage &message) override; void references(const ClangBackEnd::ReferencesMessage &message) override;
void tooltip(const ClangBackEnd::ToolTipMessage &message) override;
void followSymbol(const ClangBackEnd::FollowSymbolMessage &message) override; void followSymbol(const ClangBackEnd::FollowSymbolMessage &message) override;
private: private:
@@ -89,7 +92,7 @@ private:
CppTools::SemanticInfo::LocalUseMap localUses; CppTools::SemanticInfo::LocalUseMap localUses;
}; };
QHash<quint64, ReferencesEntry> m_referencesTable; QHash<quint64, ReferencesEntry> m_referencesTable;
QHash<quint64, QFutureInterface<CppTools::ToolTipInfo>> m_toolTipsTable;
QHash<quint64, QFutureInterface<CppTools::SymbolInfo>> m_followTable; QHash<quint64, QFutureInterface<CppTools::SymbolInfo>> m_followTable;
}; };

View File

@@ -120,6 +120,13 @@ void BackendSender::requestReferences(const RequestReferencesMessage &message)
m_connection->serverProxy().requestReferences(message); m_connection->serverProxy().requestReferences(message);
} }
void BackendSender::requestToolTip(const RequestToolTipMessage &message)
{
QTC_CHECK(m_connection->isConnected());
qCDebug(ipcLog) << ">>>" << message;
m_connection->serverProxy().requestToolTip(message);
}
void BackendSender::requestFollowSymbol(const RequestFollowSymbolMessage &message) void BackendSender::requestFollowSymbol(const RequestFollowSymbolMessage &message)
{ {
QTC_CHECK(m_connection->isConnected()); QTC_CHECK(m_connection->isConnected());

View File

@@ -48,6 +48,7 @@ public:
void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override; void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override;
void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const ClangBackEnd::RequestReferencesMessage &message) override; void requestReferences(const ClangBackEnd::RequestReferencesMessage &message) override;
void requestToolTip(const ClangBackEnd::RequestToolTipMessage &message) override;
void requestFollowSymbol(const ClangBackEnd::RequestFollowSymbolMessage &message) override; void requestFollowSymbol(const ClangBackEnd::RequestFollowSymbolMessage &message) override;
void updateVisibleTranslationUnits(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message) override;

View File

@@ -30,6 +30,7 @@ SOURCES += \
clangfixitoperationsextractor.cpp \ clangfixitoperationsextractor.cpp \
clangfollowsymbol.cpp \ clangfollowsymbol.cpp \
clangfunctionhintmodel.cpp \ clangfunctionhintmodel.cpp \
clanghoverhandler.cpp \
clangtokeninfosreporter.cpp \ clangtokeninfosreporter.cpp \
clangmodelmanagersupport.cpp \ clangmodelmanagersupport.cpp \
clangpreprocessorassistproposalitem.cpp \ clangpreprocessorassistproposalitem.cpp \
@@ -66,6 +67,7 @@ HEADERS += \
clangfixitoperationsextractor.h \ clangfixitoperationsextractor.h \
clangfollowsymbol.h \ clangfollowsymbol.h \
clangfunctionhintmodel.h \ clangfunctionhintmodel.h \
clanghoverhandler.h \
clangisdiagnosticrelatedtolocation.h \ clangisdiagnosticrelatedtolocation.h \
clangmodelmanagersupport.h \ clangmodelmanagersupport.h \
clangpreprocessorassistproposalitem.h \ clangpreprocessorassistproposalitem.h \

View File

@@ -80,6 +80,8 @@ QtcPlugin {
"clangfollowsymbol.h", "clangfollowsymbol.h",
"clangfunctionhintmodel.cpp", "clangfunctionhintmodel.cpp",
"clangfunctionhintmodel.h", "clangfunctionhintmodel.h",
"clanghoverhandler.cpp",
"clanghoverhandler.h",
"clangtokeninfosreporter.cpp", "clangtokeninfosreporter.cpp",
"clangtokeninfosreporter.h", "clangtokeninfosreporter.h",
"clangisdiagnosticrelatedtolocation.h", "clangisdiagnosticrelatedtolocation.h",

View File

@@ -375,6 +375,15 @@ ClangEditorDocumentProcessor::requestFollowSymbol(int line, int column)
static_cast<quint32>(column)); static_cast<quint32>(column));
} }
QFuture<CppTools::ToolTipInfo> ClangEditorDocumentProcessor::toolTipInfo(const QByteArray &codecName,
int line,
int column)
{
return m_communicator.requestToolTip(simpleFileContainer(codecName),
static_cast<quint32>(line),
static_cast<quint32>(column));
}
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const ClangBackEnd::FileContainer ClangEditorDocumentProcessor::fileContainerWithArguments() const
{ {
return fileContainerWithArguments(m_projectPart.data()); return fileContainerWithArguments(m_projectPart.data());
@@ -480,13 +489,19 @@ ClangEditorDocumentProcessor::creatorForHeaderErrorDiagnosticWidget(
}; };
} }
ClangBackEnd::FileContainer ClangEditorDocumentProcessor::simpleFileContainer() const ClangBackEnd::FileContainer ClangEditorDocumentProcessor::simpleFileContainer(
const QByteArray &codecName) const
{ {
Utf8String projectPartId; Utf8String projectPartId;
if (m_projectPart) if (m_projectPart)
projectPartId = m_projectPart->id(); projectPartId = m_projectPart->id();
return ClangBackEnd::FileContainer(filePath(), projectPartId, Utf8String(), false, revision()); return ClangBackEnd::FileContainer(filePath(),
projectPartId,
Utf8String(),
false,
revision(),
Utf8String::fromByteArray(codecName));
} }
static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart) static CppTools::ProjectPart projectPartForLanguageOption(CppTools::ProjectPart *projectPart)

View File

@@ -88,6 +88,9 @@ public:
QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params) override; QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params) override;
QFuture<CppTools::CursorInfo> requestLocalReferences(const QTextCursor &cursor) override; QFuture<CppTools::CursorInfo> requestLocalReferences(const QTextCursor &cursor) override;
QFuture<CppTools::SymbolInfo> requestFollowSymbol(int line, int column) override; QFuture<CppTools::SymbolInfo> requestFollowSymbol(int line, int column) override;
QFuture<CppTools::ToolTipInfo> toolTipInfo(const QByteArray &codecName,
int line,
int column) override;
ClangBackEnd::FileContainer fileContainerWithArguments() const; ClangBackEnd::FileContainer fileContainerWithArguments() const;
@@ -106,7 +109,7 @@ private:
void requestDocumentAnnotations(const QString &projectpartId); void requestDocumentAnnotations(const QString &projectpartId);
HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget( HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget(
const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic); const ClangBackEnd::DiagnosticContainer &firstHeaderErrorDiagnostic);
ClangBackEnd::FileContainer simpleFileContainer() const; ClangBackEnd::FileContainer simpleFileContainer(const QByteArray &codecName = QByteArray()) const;
ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const; ClangBackEnd::FileContainer fileContainerWithArguments(CppTools::ProjectPart *projectPart) const;
ClangBackEnd::FileContainer fileContainerWithArgumentsAndDocumentContent( ClangBackEnd::FileContainer fileContainerWithArgumentsAndDocumentContent(
CppTools::ProjectPart *projectPart) const; CppTools::ProjectPart *projectPart) const;

View File

@@ -0,0 +1,232 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clanghoverhandler.h"
#include <coreplugin/helpmanager.h>
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/editordocumenthandle.h>
#include <texteditor/texteditor.h>
#include <utils/qtcassert.h>
#include <utils/textutils.h>
#include <utils/tooltip/tooltip.h>
#include <QFutureWatcher>
#include <QLoggingCategory>
#include <QTextCodec>
#include <QVBoxLayout>
Q_LOGGING_CATEGORY(hoverLog, "qtc.clangcodemodel.hover");
using namespace TextEditor;
namespace ClangCodeModel {
namespace Internal {
static CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor(TextEditorWidget *editorWidget)
{
const QString filePath = editorWidget->textDocument()->filePath().toString();
auto cppModelManager = CppTools::CppModelManager::instance();
CppTools::CppEditorDocumentHandle *editorHandle = cppModelManager->cppEditorDocument(filePath);
if (editorHandle)
return editorHandle->processor();
return 0;
}
static bool editorDocumentProcessorHasDiagnosticAt(TextEditorWidget *editorWidget, int pos)
{
if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
int line, column;
if (Utils::Text::convertPosition(editorWidget->document(), pos, &line, &column))
return processor->hasDiagnosticsAt(line, column);
}
return false;
}
static void processWithEditorDocumentProcessor(TextEditorWidget *editorWidget,
const QPoint &point,
int position,
const QString &helpId)
{
if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
int line, column;
if (Utils::Text::convertPosition(editorWidget->document(), position, &line, &column)) {
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(2);
processor->addDiagnosticToolTipToLayout(line, column, layout);
Utils::ToolTip::show(point, layout, editorWidget, helpId);
}
}
}
static QFuture<CppTools::ToolTipInfo> editorDocumentHandlesToolTipInfo(
TextEditorWidget *editorWidget, int pos)
{
const QByteArray textCodecName = editorWidget->textDocument()->codec()->name();
if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
int line, column;
if (Utils::Text::convertPosition(editorWidget->document(), pos, &line, &column))
return processor->toolTipInfo(textCodecName, line, column + 1);
}
return QFuture<CppTools::ToolTipInfo>();
}
ClangHoverHandler::ClangHoverHandler()
{
setIsAsyncHandler(true);
}
ClangHoverHandler::~ClangHoverHandler()
{
cancelAsyncCheck();
}
void ClangHoverHandler::identifyMatchAsync(TextEditorWidget *editorWidget,
int pos,
BaseHoverHandler::ReportPriority report)
{
// Reset
m_futureWatcher.reset();
m_cursorPosition = -1;
// Check for diagnostics (sync)
if (editorDocumentProcessorHasDiagnosticAt(editorWidget, pos)) {
qCDebug(hoverLog) << "Checking for diagnostic at" << pos;
setPriority(Priority_Diagnostic);
m_cursorPosition = pos;
report(priority());
return;
}
// Check for tooltips (async)
QFuture<CppTools::ToolTipInfo> future = editorDocumentHandlesToolTipInfo(editorWidget, pos);
if (QTC_GUARD(future.isRunning())) {
qCDebug(hoverLog) << "Requesting tooltip info at" << pos;
m_reportPriority = report;
m_futureWatcher.reset(new QFutureWatcher<CppTools::ToolTipInfo>());
QObject::connect(m_futureWatcher.data(), &QFutureWatcherBase::finished, [this]() {
processToolTipInfo(m_futureWatcher->result());
});
m_futureWatcher->setFuture(future);
return;
}
report(Priority_None); // Ops, something went wrong.
}
void ClangHoverHandler::cancelAsyncCheck()
{
if (m_futureWatcher)
m_futureWatcher->cancel();
}
#define RETURN_TEXT_FOR_CASE(enumValue) case TextEditor::HelpItem::enumValue: return #enumValue
static const char *helpItemCategoryAsString(TextEditor::HelpItem::Category category)
{
switch (category) {
RETURN_TEXT_FOR_CASE(Unknown);
RETURN_TEXT_FOR_CASE(ClassOrNamespace);
RETURN_TEXT_FOR_CASE(Enum);
RETURN_TEXT_FOR_CASE(Typedef);
RETURN_TEXT_FOR_CASE(Macro);
RETURN_TEXT_FOR_CASE(Brief);
RETURN_TEXT_FOR_CASE(Function);
RETURN_TEXT_FOR_CASE(QmlComponent);
RETURN_TEXT_FOR_CASE(QmlProperty);
RETURN_TEXT_FOR_CASE(QMakeVariableOfFunction);
}
return "UnhandledHelpItemCategory";
}
#undef RETURN_TEXT_FOR_CASE
void ClangHoverHandler::processToolTipInfo(const CppTools::ToolTipInfo &info)
{
qCDebug(hoverLog) << "Processing tooltip info" << info.text;
QString text = info.text;
if (!info.briefComment.isEmpty())
text.append("\n\n" + info.briefComment);
for (const QString &qdocIdCandidate : info.qDocIdCandidates) {
qCDebug(hoverLog) << "Querying help manager with"
<< qdocIdCandidate
<< info.qDocMark
<< helpItemCategoryAsString(info.qDocCategory);
const QMap<QString, QUrl> helpLinks = Core::HelpManager::linksForIdentifier(qdocIdCandidate);
if (!helpLinks.isEmpty()) {
qCDebug(hoverLog) << " Match!";
setLastHelpItemIdentified(
HelpItem(qdocIdCandidate, info.qDocMark, info.qDocCategory, helpLinks));
break;
}
}
if (!info.sizeInBytes.isEmpty())
text.append(tr("\n\n%1 bytes").arg(info.sizeInBytes));
setToolTip(text);
m_reportPriority(priority());
}
void ClangHoverHandler::decorateToolTip()
{
if (priority() == Priority_Diagnostic)
return;
if (Qt::mightBeRichText(toolTip()))
setToolTip(toolTip().toHtmlEscaped());
const HelpItem &help = lastHelpItemIdentified();
if (help.isValid()) {
const QString text = CppTools::CppHoverHandler::tooltipTextForHelpItem(help);
if (!text.isEmpty())
setToolTip(text);
}
}
void ClangHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget,
const QPoint &point)
{
if (priority() == Priority_Diagnostic) {
const HelpItem helpItem = lastHelpItemIdentified();
const QString helpId = helpItem.isValid() ? helpItem.helpId() : QString();
processWithEditorDocumentProcessor(editorWidget, point, m_cursorPosition, helpId);
return;
}
// Priority_Tooltip / Priority_Help
BaseHoverHandler::operateTooltip(editorWidget, point);
}
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -0,0 +1,60 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/cpphoverhandler.h>
#include <texteditor/basehoverhandler.h>
namespace ClangCodeModel {
namespace Internal {
class ClangHoverHandler : public TextEditor::BaseHoverHandler
{
Q_DECLARE_TR_FUNCTIONS(ClangHoverHandler)
public:
ClangHoverHandler();
~ClangHoverHandler() override;
void identifyMatchAsync(TextEditor::TextEditorWidget *editorWidget,
int pos,
ReportPriority report) override;
void decorateToolTip() override;
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
private:
void cancelAsyncCheck() override;
void processToolTipInfo(const CppTools::ToolTipInfo &info);
private:
int m_cursorPosition = -1;
QScopedPointer<QFutureWatcher<CppTools::ToolTipInfo>> m_futureWatcher;
ReportPriority m_reportPriority;
};
} // namespace Internal
} // namespace ClangCodeModel

View File

@@ -29,11 +29,11 @@
#include "clangeditordocumentprocessor.h" #include "clangeditordocumentprocessor.h"
#include "clangutils.h" #include "clangutils.h"
#include "clangfollowsymbol.h" #include "clangfollowsymbol.h"
#include "clanghoverhandler.h"
#include "clangrefactoringengine.h" #include "clangrefactoringengine.h"
#include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/editormanager.h>
#include <cpptools/cppfollowsymbolundercursor.h> #include <cpptools/cppfollowsymbolundercursor.h>
#include <cpptools/cpphoverhandler.h>
#include <cpptools/cppmodelmanager.h> #include <cpptools/cppmodelmanager.h>
#include <cpptools/editordocumenthandle.h> #include <cpptools/editordocumenthandle.h>
#include <cpptools/projectinfo.h> #include <cpptools/projectinfo.h>
@@ -114,7 +114,7 @@ CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssis
TextEditor::BaseHoverHandler *ModelManagerSupportClang::createHoverHandler() TextEditor::BaseHoverHandler *ModelManagerSupportClang::createHoverHandler()
{ {
return new CppTools::CppHoverHandler; return new Internal::ClangHoverHandler;
} }
CppTools::FollowSymbolInterface &ModelManagerSupportClang::followSymbolInterface() CppTools::FollowSymbolInterface &ModelManagerSupportClang::followSymbolInterface()

View File

@@ -97,6 +97,13 @@ void BaseEditorDocumentProcessor::setParserConfig(
parser()->setConfiguration(config); parser()->setConfiguration(config);
} }
QFuture<ToolTipInfo> BaseEditorDocumentProcessor::toolTipInfo(const QByteArray &/*codecName*/,
int /*line*/,
int /*column*/)
{
return QFuture<ToolTipInfo>();
}
void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future, void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
BaseEditorDocumentParser::Ptr parser, BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams) BaseEditorDocumentParser::UpdateParams updateParams)

View File

@@ -32,6 +32,7 @@
#include "cpptools_global.h" #include "cpptools_global.h"
#include <texteditor/codeassist/assistinterface.h> #include <texteditor/codeassist/assistinterface.h>
#include <texteditor/helpitem.h>
#include <texteditor/quickfix.h> #include <texteditor/quickfix.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <texteditor/textdocument.h> #include <texteditor/textdocument.h>
@@ -48,6 +49,18 @@ class TextDocument;
namespace CppTools { namespace CppTools {
// For clang code model only, move?
struct CPPTOOLS_EXPORT ToolTipInfo {
QString text;
QString briefComment;
QStringList qDocIdCandidates;
QString qDocMark;
TextEditor::HelpItem::Category qDocCategory;
QString sizeInBytes;
};
class CPPTOOLS_EXPORT BaseEditorDocumentProcessor : public QObject class CPPTOOLS_EXPORT BaseEditorDocumentProcessor : public QObject
{ {
Q_OBJECT Q_OBJECT
@@ -78,6 +91,7 @@ public:
virtual QFuture<CursorInfo> cursorInfo(const CursorInfoParams &params) = 0; virtual QFuture<CursorInfo> cursorInfo(const CursorInfoParams &params) = 0;
virtual QFuture<CursorInfo> requestLocalReferences(const QTextCursor &cursor) = 0; virtual QFuture<CursorInfo> requestLocalReferences(const QTextCursor &cursor) = 0;
virtual QFuture<SymbolInfo> requestFollowSymbol(int line, int column) = 0; virtual QFuture<SymbolInfo> requestFollowSymbol(int line, int column) = 0;
virtual QFuture<ToolTipInfo> toolTipInfo(const QByteArray &codecName, int line, int column);
public: public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>; using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;

View File

@@ -28,78 +28,47 @@
#include "cppelementevaluator.h" #include "cppelementevaluator.h"
#include <coreplugin/helpmanager.h> #include <coreplugin/helpmanager.h>
#include <cpptools/baseeditordocumentprocessor.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/editordocumenthandle.h>
#include <texteditor/texteditor.h> #include <texteditor/texteditor.h>
#include <utils/textutils.h> #include <utils/textutils.h>
#include <utils/qtcassert.h>
#include <utils/tooltip/tooltip.h>
#include <QTextCursor> #include <QTextCursor>
#include <QUrl> #include <QUrl>
#include <QVBoxLayout>
using namespace Core; using namespace Core;
using namespace TextEditor; using namespace TextEditor;
namespace {
CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor(TextEditorWidget *editorWidget)
{
const QString filePath = editorWidget->textDocument()->filePath().toString();
auto cppModelManager = CppTools::CppModelManager::instance();
CppTools::CppEditorDocumentHandle *editorHandle = cppModelManager->cppEditorDocument(filePath);
if (editorHandle)
return editorHandle->processor();
return 0;
}
bool editorDocumentProcessorHasDiagnosticAt(TextEditorWidget *editorWidget, int pos)
{
if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
int line, column;
if (Utils::Text::convertPosition(editorWidget->document(), pos, &line, &column))
return processor->hasDiagnosticsAt(line, column);
}
return false;
}
void processWithEditorDocumentProcessor(TextEditorWidget *editorWidget,
const QPoint &point,
int position,
const QString &helpId)
{
if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
int line, column;
if (Utils::Text::convertPosition(editorWidget->document(), position, &line, &column)) {
auto layout = new QVBoxLayout;
layout->setContentsMargins(0, 0, 0, 0);
layout->setSpacing(2);
processor->addDiagnosticToolTipToLayout(line, column, layout);
Utils::ToolTip::show(point, layout, editorWidget, helpId);
}
}
}
} // anonymous namespace
namespace CppTools { namespace CppTools {
QString CppHoverHandler::tooltipTextForHelpItem(const HelpItem &helpItem)
{
// If Qt is built with a namespace, we still show the tip without it, as
// it is in the docs and for consistency with the doc extraction mechanism.
const HelpItem::Category category = helpItem.category();
const QString &contents = helpItem.extractContent(false);
if (!contents.isEmpty()) {
if (category == HelpItem::ClassOrNamespace)
return helpItem.helpId() + contents;
else
return contents;
} else if (category == HelpItem::Typedef ||
category == HelpItem::Enum ||
category == HelpItem::ClassOrNamespace) {
// This approach is a bit limited since it cannot be used for functions
// because the help id doesn't really help in that case.
QString prefix;
if (category == HelpItem::Typedef)
prefix = QLatin1String("typedef ");
else if (category == HelpItem::Enum)
prefix = QLatin1String("enum ");
return prefix + helpItem.helpId();
}
return QString();
}
void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos) void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
{ {
m_positionForEditorDocumentProcessor = -1;
if (editorDocumentProcessorHasDiagnosticAt(editorWidget, pos)) {
setPriority(Priority_Diagnostic);
m_positionForEditorDocumentProcessor = pos;
return;
}
QTextCursor tc(editorWidget->document()); QTextCursor tc(editorWidget->document());
tc.setPosition(pos); tc.setPosition(pos);
@@ -135,9 +104,6 @@ void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
void CppHoverHandler::decorateToolTip() void CppHoverHandler::decorateToolTip()
{ {
if (m_positionForEditorDocumentProcessor != -1)
return;
if (Qt::mightBeRichText(toolTip())) if (Qt::mightBeRichText(toolTip()))
setToolTip(toolTip().toHtmlEscaped()); setToolTip(toolTip().toHtmlEscaped());
@@ -146,42 +112,10 @@ void CppHoverHandler::decorateToolTip()
const HelpItem &help = lastHelpItemIdentified(); const HelpItem &help = lastHelpItemIdentified();
if (help.isValid()) { if (help.isValid()) {
// If Qt is built with a namespace, we still show the tip without it, as const QString text = tooltipTextForHelpItem(help);
// it is in the docs and for consistency with the doc extraction mechanism. if (!text.isEmpty())
const HelpItem::Category category = help.category(); setToolTip(text);
const QString &contents = help.extractContent(false);
if (!contents.isEmpty()) {
if (category == HelpItem::ClassOrNamespace)
setToolTip(help.helpId() + contents);
else
setToolTip(contents);
} else if (category == HelpItem::Typedef ||
category == HelpItem::Enum ||
category == HelpItem::ClassOrNamespace) {
// This approach is a bit limited since it cannot be used for functions
// because the help id doesn't really help in that case.
QString prefix;
if (category == HelpItem::Typedef)
prefix = QLatin1String("typedef ");
else if (category == HelpItem::Enum)
prefix = QLatin1String("enum ");
setToolTip(prefix + help.helpId());
}
} }
} }
void CppHoverHandler::operateTooltip(TextEditor::TextEditorWidget *editorWidget,
const QPoint &point)
{
if (m_positionForEditorDocumentProcessor == -1) {
BaseHoverHandler::operateTooltip(editorWidget, point);
return;
}
const HelpItem helpItem = lastHelpItemIdentified();
const QString helpId = helpItem.isValid() ? helpItem.helpId() : QString();
processWithEditorDocumentProcessor(editorWidget, point, m_positionForEditorDocumentProcessor,
helpId);
}
} // namespace CppTools } // namespace CppTools

View File

@@ -33,13 +33,12 @@ namespace CppTools {
class CPPTOOLS_EXPORT CppHoverHandler : public TextEditor::BaseHoverHandler class CPPTOOLS_EXPORT CppHoverHandler : public TextEditor::BaseHoverHandler
{ {
public:
static QString tooltipTextForHelpItem(const TextEditor::HelpItem &help);
private: private:
void identifyMatch(TextEditor::TextEditorWidget *editorWidget, int pos) override; void identifyMatch(TextEditor::TextEditorWidget *editorWidget, int pos) override;
void decorateToolTip() override; void decorateToolTip() override;
void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
private:
int m_positionForEditorDocumentProcessor = -1;
}; };
} // namespace CppTools } // namespace CppTools

View File

@@ -309,6 +309,7 @@ public:
const int documentRevision = textCursor.document()->revision(); const int documentRevision = textCursor.document()->revision();
const int position = Text::wordStartCursor(textCursor).position(); const int position = Text::wordStartCursor(textCursor).position();
if (m_lastHandlerInfo.applies(documentRevision, position)) { if (m_lastHandlerInfo.applies(documentRevision, position)) {
qDebug() << "Last handler applies, showing it";
m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false); m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
return; return;
} }
@@ -364,6 +365,7 @@ public:
// All were queried, run the best // All were queried, run the best
if (m_bestHandler) { if (m_bestHandler) {
qDebug() << "setting last handler info:" << m_documentRevision << m_position;
m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position); m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
m_bestHandler->showToolTip(m_widget, m_point); m_bestHandler->showToolTip(m_widget, m_point);
} }

View File

@@ -36,4 +36,9 @@ enum class PreferredTranslationUnit
LastUninitialized, LastUninitialized,
}; };
// CLANG-UPGRADE-CHECK: Remove IS_SUSPEND_SUPPORTED once we require clang >= 7.0
#if defined(CINDEX_VERSION_HAS_PRETTYDECL_BACKPORTED) || CINDEX_VERSION_MINOR >= 47
# define IS_PRETTY_DECL_SUPPORTED
#endif
} // namespace ClangBackEnd } // namespace ClangBackEnd

View File

@@ -29,10 +29,12 @@ HEADERS += \
$$PWD/clangreparsesupportivetranslationunitjob.h \ $$PWD/clangreparsesupportivetranslationunitjob.h \
$$PWD/clangrequestdocumentannotationsjob.h \ $$PWD/clangrequestdocumentannotationsjob.h \
$$PWD/clangrequestreferencesjob.h \ $$PWD/clangrequestreferencesjob.h \
$$PWD/clangrequesttooltipjob.h \
$$PWD/clangresumedocumentjob.h \ $$PWD/clangresumedocumentjob.h \
$$PWD/clangstring.h \ $$PWD/clangstring.h \
$$PWD/clangsupportivetranslationunitinitializer.h \ $$PWD/clangsupportivetranslationunitinitializer.h \
$$PWD/clangsuspenddocumentjob.h \ $$PWD/clangsuspenddocumentjob.h \
$$PWD/clangtooltipinfocollector.h \
$$PWD/clangtranslationunit.h \ $$PWD/clangtranslationunit.h \
$$PWD/clangtranslationunits.h \ $$PWD/clangtranslationunits.h \
$$PWD/clangtranslationunitupdater.h \ $$PWD/clangtranslationunitupdater.h \
@@ -86,8 +88,10 @@ SOURCES += \
$$PWD/clangreparsesupportivetranslationunitjob.cpp \ $$PWD/clangreparsesupportivetranslationunitjob.cpp \
$$PWD/clangrequestdocumentannotationsjob.cpp \ $$PWD/clangrequestdocumentannotationsjob.cpp \
$$PWD/clangrequestreferencesjob.cpp \ $$PWD/clangrequestreferencesjob.cpp \
$$PWD/clangrequesttooltipjob.cpp \
$$PWD/clangsuspenddocumentjob.cpp \ $$PWD/clangsuspenddocumentjob.cpp \
$$PWD/clangsupportivetranslationunitinitializer.cpp \ $$PWD/clangsupportivetranslationunitinitializer.cpp \
$$PWD/clangtooltipinfocollector.cpp \
$$PWD/clangtranslationunit.cpp \ $$PWD/clangtranslationunit.cpp \
$$PWD/clangtranslationunits.cpp \ $$PWD/clangtranslationunits.cpp \
$$PWD/clangtranslationunitupdater.cpp \ $$PWD/clangtranslationunitupdater.cpp \

View File

@@ -258,6 +258,7 @@ static void fillJobRequest(JobRequest &jobRequest, const MessageType &message)
jobRequest.line = message.line(); jobRequest.line = message.line();
jobRequest.column = message.column(); jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber(); jobRequest.ticketNumber = message.ticketNumber();
jobRequest.textCodecName = message.fileContainer().textCodecName();
// The unsaved files might get updater later, so take the current // The unsaved files might get updater later, so take the current
// revision for the request. // revision for the request.
jobRequest.documentRevision = message.fileContainer().documentRevision(); jobRequest.documentRevision = message.fileContainer().documentRevision();
@@ -303,6 +304,25 @@ void ClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMessage
} }
} }
void ClangCodeModelServer::requestToolTip(const RequestToolTipMessage &message)
{
TIME_SCOPE_DURATION("ClangCodeModelServer::requestToolTip");
try {
const Document document = documents.document(message.fileContainer().filePath(),
message.fileContainer().projectPartId());
DocumentProcessor processor = documentProcessors().processor(document);
JobRequest jobRequest = processor.createJobRequest(JobRequest::Type::RequestToolTip);
fillJobRequest(jobRequest, message);
processor.addJob(jobRequest);
processor.process();
} catch (const std::exception &exception) {
qWarning() << "Error in ClangCodeModelServer::requestToolTip:" << exception.what();
}
}
void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) void ClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{ {
qCDebug(serverLog) << "########## updateVisibleTranslationUnits"; qCDebug(serverLog) << "########## updateVisibleTranslationUnits";

View File

@@ -61,6 +61,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override; void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override; void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
void requestToolTip(const RequestToolTipMessage &message) override;
public: // for tests public: // for tests
const Documents &documentsForTestOnly() const; const Documents &documentsForTestOnly() const;

View File

@@ -32,6 +32,7 @@
#include "clangreparsesupportivetranslationunitjob.h" #include "clangreparsesupportivetranslationunitjob.h"
#include "clangrequestdocumentannotationsjob.h" #include "clangrequestdocumentannotationsjob.h"
#include "clangrequestreferencesjob.h" #include "clangrequestreferencesjob.h"
#include "clangrequesttooltipjob.h"
#include "clangresumedocumentjob.h" #include "clangresumedocumentjob.h"
#include "clangsuspenddocumentjob.h" #include "clangsuspenddocumentjob.h"
#include "clangupdatedocumentannotationsjob.h" #include "clangupdatedocumentannotationsjob.h"
@@ -40,6 +41,7 @@
#include <clangsupport/cmbcodecompletedmessage.h> #include <clangsupport/cmbcodecompletedmessage.h>
#include <clangsupport/followsymbolmessage.h> #include <clangsupport/followsymbolmessage.h>
#include <clangsupport/referencesmessage.h> #include <clangsupport/referencesmessage.h>
#include <clangsupport/tooltipmessage.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
@@ -62,6 +64,7 @@ static const char *JobRequestTypeToText(JobRequest::Type type)
RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations); RETURN_TEXT_FOR_CASE(RequestDocumentAnnotations);
RETURN_TEXT_FOR_CASE(RequestReferences); RETURN_TEXT_FOR_CASE(RequestReferences);
RETURN_TEXT_FOR_CASE(FollowSymbol); RETURN_TEXT_FOR_CASE(FollowSymbol);
RETURN_TEXT_FOR_CASE(RequestToolTip);
RETURN_TEXT_FOR_CASE(SuspendDocument); RETURN_TEXT_FOR_CASE(SuspendDocument);
RETURN_TEXT_FOR_CASE(ResumeDocument); RETURN_TEXT_FOR_CASE(ResumeDocument);
} }
@@ -126,6 +129,7 @@ static JobRequest::ExpirationConditions expirationConditionsForType(JobRequest::
return Conditions(Condition::AnythingChanged); return Conditions(Condition::AnythingChanged);
case Type::RequestReferences: case Type::RequestReferences:
case Type::RequestDocumentAnnotations: case Type::RequestDocumentAnnotations:
case Type::RequestToolTip:
case Type::FollowSymbol: case Type::FollowSymbol:
return Conditions(Condition::DocumentClosed) return Conditions(Condition::DocumentClosed)
| Conditions(Condition::DocumentRevisionChanged); | Conditions(Condition::DocumentRevisionChanged);
@@ -153,8 +157,10 @@ static JobRequest::RunConditions conditionsForType(JobRequest::Type type)
Conditions conditions = Conditions(Condition::DocumentUnsuspended) Conditions conditions = Conditions(Condition::DocumentUnsuspended)
| Conditions(Condition::DocumentVisible); | Conditions(Condition::DocumentVisible);
if (type == Type::RequestReferences || type == Type::FollowSymbol) if (type == Type::RequestReferences || type == Type::FollowSymbol
|| type == Type::RequestToolTip) {
conditions |= Condition::CurrentDocumentRevision; conditions |= Condition::CurrentDocumentRevision;
}
if (type != Type::UpdateDocumentAnnotations && type != Type::ParseSupportiveTranslationUnit) if (type != Type::UpdateDocumentAnnotations && type != Type::ParseSupportiveTranslationUnit)
conditions |= Condition::DocumentParsed; conditions |= Condition::DocumentParsed;
@@ -192,6 +198,8 @@ IAsyncJob *JobRequest::createJob() const
return new RequestDocumentAnnotationsJob(); return new RequestDocumentAnnotationsJob();
case JobRequest::Type::RequestReferences: case JobRequest::Type::RequestReferences:
return new RequestReferencesJob(); return new RequestReferencesJob();
case JobRequest::Type::RequestToolTip:
return new RequestToolTipJob();
case JobRequest::Type::FollowSymbol: case JobRequest::Type::FollowSymbol:
return new FollowSymbolJob(); return new FollowSymbolJob();
case JobRequest::Type::SuspendDocument: case JobRequest::Type::SuspendDocument:
@@ -224,6 +232,11 @@ void JobRequest::cancelJob(ClangCodeModelClientInterface &client) const
false, false,
ticketNumber)); ticketNumber));
break; break;
case JobRequest::Type::RequestToolTip:
client.tooltip(ToolTipMessage(FileContainer(),
ToolTipInfo(),
ticketNumber));
break;
case JobRequest::Type::CompleteCode: case JobRequest::Type::CompleteCode:
client.codeCompleted(CodeCompletedMessage(CodeCompletions(), client.codeCompleted(CodeCompletedMessage(CodeCompletions(),
CompletionCorrection::NoCorrection, CompletionCorrection::NoCorrection,

View File

@@ -59,6 +59,7 @@ public:
RequestDocumentAnnotations, RequestDocumentAnnotations,
RequestReferences, RequestReferences,
FollowSymbol, FollowSymbol,
RequestToolTip,
SuspendDocument, SuspendDocument,
ResumeDocument, ResumeDocument,
@@ -118,6 +119,7 @@ public:
qint32 funcNameStartLine = -1; qint32 funcNameStartLine = -1;
qint32 funcNameStartColumn = -1; qint32 funcNameStartColumn = -1;
quint64 ticketNumber = 0; quint64 ticketNumber = 0;
Utf8String textCodecName;
bool localReferences = false; bool localReferences = false;
}; };

View File

@@ -0,0 +1,67 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangrequesttooltipjob.h"
#include <clangsupport/clangsupportdebugutils.h>
#include <clangsupport/clangcodemodelclientinterface.h>
#include <clangsupport/tooltipmessage.h>
#include <utils/qtcassert.h>
namespace ClangBackEnd {
IAsyncJob::AsyncPrepareResult RequestToolTipJob::prepareAsyncRun()
{
const JobRequest jobRequest = context().jobRequest;
QTC_ASSERT(jobRequest.type == JobRequest::Type::RequestToolTip, return AsyncPrepareResult());
QTC_ASSERT(acquireDocument(), return AsyncPrepareResult());
const TranslationUnit translationUnit = *m_translationUnit;
const UnsavedFiles unsavedFiles = *context().unsavedFiles;
const quint32 line = jobRequest.line;
const quint32 column = jobRequest.column;
const Utf8String textCodecName = jobRequest.textCodecName;
setRunner([translationUnit, unsavedFiles, line, column, textCodecName]() {
TIME_SCOPE_DURATION("RequestToolTipJobRunner");
UnsavedFiles theUnsavedFiles = unsavedFiles;
return translationUnit.tooltip(theUnsavedFiles, textCodecName, line, column);
});
return AsyncPrepareResult{translationUnit.id()};
}
void RequestToolTipJob::finalizeAsyncRun()
{
if (!context().isOutdated()) {
const AsyncResult result = asyncResult();
context().client->tooltip(ToolTipMessage(m_pinnedFileContainer,
result,
context().jobRequest.ticketNumber));
}
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,43 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <clangsupport/tooltipinfo.h>
#include "clangdocumentjob.h"
namespace ClangBackEnd {
class RequestToolTipJob : public DocumentJob<ToolTipInfo>
{
public:
using AsyncResult = ToolTipInfo;
AsyncPrepareResult prepareAsyncRun() override;
void finalizeAsyncRun() override;
};
} // namespace ClangBackEnd

View File

@@ -0,0 +1,531 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "clangtooltipinfocollector.h"
#include "clangbackend_global.h"
#include "clangstring.h"
#include "cursor.h"
#include "sourcerange.h"
#include "unsavedfiles.h"
#include "unsavedfile.h"
#include <clangsupport/sourcerangecontainer.h>
#include <utils/qtcassert.h>
#include <utils/textfileformat.h>
#include <utf8string.h>
#include <QDebug>
#include <QDir>
#include <QTextCodec>
namespace ClangBackEnd {
namespace {
Utf8StringVector qualificationPrefixAsVector(const Cursor &cursor)
{
Utf8StringVector result;
for (Cursor parent = cursor.semanticParent();
parent.isValid() && (parent.kind() == CXCursor_Namespace || parent.isCompoundType());
parent = parent.semanticParent()) {
result.prepend(parent.spelling());
}
return result;
}
Utf8String qualificationPrefix(const Cursor &cursor)
{
// TODO: Implement with qualificationPrefixAsVector()
Utf8String qualifiedName;
for (Cursor parent = cursor.semanticParent();
parent.isValid() && (parent.kind() == CXCursor_Namespace);
parent = parent.semanticParent()) {
qualifiedName = parent.spelling() + Utf8StringLiteral("::") + qualifiedName;
}
return qualifiedName;
}
Utf8String displayName(const Cursor &cursor)
{
if (cursor.kind() == CXCursor_ClassTemplate) {
// TODO: The qualification should be part of the display name. Fix this in libclang.
return qualificationPrefix(cursor) + cursor.displayName();
}
return cursor.displayName();
}
Utf8String textForFunctionLike(const Cursor &cursor)
{
#ifdef IS_PRETTY_DECL_SUPPORTED
CXPrintingPolicy policy = clang_getCursorPrintingPolicy(cursor.cx());
clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_FullyQualifiedName, 1);
clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_TerseOutput, 1);
// Avoid printing attributes/pragmas
clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_PolishForDeclaration, 1);
clang_PrintingPolicy_setProperty(policy, CXPrintingPolicy_SuppressInitializers, 1);
const Utf8String prettyPrinted = ClangString(
clang_getCursorPrettyPrinted(cursor.cx(), policy));
clang_PrintingPolicy_dispose(policy);
return prettyPrinted;
#else
// Printing function declarations with displayName() is quite limited:
// * result type is not included
// * parameter names are not included
// * templates in the result type are not included
// * no full qualification of the function name
return Utf8String(cursor.resultType().spelling())
+ Utf8StringLiteral(" ")
+ qualificationPrefix(cursor)
+ Utf8String(cursor.displayName());
#endif
}
Utf8String textForEnumConstantDecl(const Cursor &cursor)
{
const Cursor semanticParent = cursor.semanticParent();
QTC_ASSERT(semanticParent.kind() == CXCursor_EnumDecl, return Utf8String());
const Type enumType = semanticParent.enumType();
if (enumType.isUnsigned())
return Utf8String::number(cursor.enumConstantUnsignedValue());
return Utf8String::number(cursor.enumConstantValue());
}
Utf8String textForInclusionDirective(const Cursor &cursor)
{
const CXFile includedFile = cursor.includedFile();
const Utf8String fileName = ClangString(clang_getFileName(includedFile));
return QDir::toNativeSeparators(fileName.toString());
}
Utf8String textForAnyTypeAlias(const Cursor &cursor)
{
// For a CXCursor_TypeAliasTemplateDecl the type of cursor/referenced
// is invalid, so we do not get the underlying type. This here solely
// reports the unresolved name instead of the empty string.
if (cursor.kind() == CXCursor_TypeAliasTemplateDecl)
return cursor.displayName();
return cursor.type().alias().utf8Spelling();
}
bool includeSizeForCursor(const Cursor &cursor)
{
return cursor.isCompoundType()
|| cursor.kind() == CXCursor_EnumDecl
|| cursor.kind() == CXCursor_UnionDecl
|| cursor.kind() == CXCursor_FieldDecl;
}
Utf8String sizeInBytes(const Cursor &cursor)
{
if (includeSizeForCursor(cursor)) {
bool ok = false;
const long long size = cursor.type().sizeOf(&ok);
if (ok)
return Utf8String::number(size);
}
return Utf8String();
}
Cursor referencedCursor(const Cursor &cursor)
{
// Query the referenced cursor directly instead of first testing with cursor.isReference().
// cursor.isReference() reports false for e.g. CXCursor_DeclRefExpr or CXCursor_CallExpr
// although it returns a valid cursor.
const Cursor referenced = cursor.referenced();
if (referenced.isValid())
return referenced;
const Cursor definition = cursor.definition();
if (definition.isValid())
return definition;
return cursor;
}
class ToolTipInfoCollector
{
public:
ToolTipInfoCollector(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
const Utf8String &mainFilePath,
CXTranslationUnit cxTranslationUnit);
ToolTipInfo collect(uint line, uint column) const;
private:
Utf8String text(const Cursor &cursor, const Cursor &referenced) const;
Utf8String textForMacroExpansion(const Cursor &cursor) const;
Utf8String textForNamespaceAlias(const Cursor &cursor) const;
ToolTipInfo qDocInfo(const Cursor &cursor) const;
CXSourceLocation toCXSourceLocation(uint line, uint column) const;
UnsavedFile unsavedFile(const Utf8String &filePath) const;
Utf8String lineRange(const Utf8String &filePath, unsigned fromLine, unsigned toLine) const;
private:
UnsavedFiles &m_unsavedFiles;
const Utf8String m_textCodecName;
const Utf8String m_mainFilePath;
CXTranslationUnit m_cxTranslationUnit = nullptr;
};
ToolTipInfoCollector::ToolTipInfoCollector(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
const Utf8String &mainFilePath,
CXTranslationUnit cxTranslationUnit)
: m_unsavedFiles(unsavedFiles)
, m_textCodecName(textCodecName)
, m_mainFilePath(mainFilePath)
, m_cxTranslationUnit(cxTranslationUnit)
{
}
Utf8String ToolTipInfoCollector::text(const Cursor &cursor, const Cursor &referenced) const
{
if (cursor.kind() == CXCursor_MacroExpansion)
return textForMacroExpansion(referenced);
if (referenced.kind() == CXCursor_EnumConstantDecl)
return textForEnumConstantDecl(referenced);
if (referenced.kind() == CXCursor_InclusionDirective)
return textForInclusionDirective(referenced);
if (referenced.kind() == CXCursor_Namespace)
return qualificationPrefix(referenced) + referenced.spelling();
if (referenced.kind() == CXCursor_NamespaceAlias)
return textForNamespaceAlias(referenced);
if (referenced.isAnyTypeAlias())
return textForAnyTypeAlias(referenced);
if (referenced.isFunctionLike())
return textForFunctionLike(referenced);
if (referenced.type().canonical().isBuiltinType())
return referenced.type().canonical().builtinTypeToString();
if (referenced.kind() == CXCursor_VarDecl)
return referenced.type().spelling(); // e.g. "Zii<int>"
const Type referencedType = referenced.type();
if (referencedType.isValid()) {
// Generally, the type includes the qualification but has this limitations:
// * namespace aliases are not resolved
// * outer class of a inner template class is not included
// The type includes the qualification, but not resolved namespace aliases.
// For a CXType_Record, this also includes e.g. "const " as prefix.
return referencedType.canonical().utf8Spelling();
}
return displayName(referenced);
}
Utf8String ToolTipInfoCollector::textForMacroExpansion(const Cursor &cursor) const
{
QTC_ASSERT(cursor.kind() == CXCursor_MacroDefinition, return Utf8String());
const SourceRange sourceRange = cursor.sourceRange();
const SourceLocation start = sourceRange.start();
const SourceLocation end = sourceRange.end();
return lineRange(start.filePath(), start.line(), end.line());
}
Utf8String ToolTipInfoCollector::textForNamespaceAlias(const Cursor &cursor) const
{
// TODO: Add some libclang API to get the aliased name straight away.
CXToken *cxTokens = nullptr;
uint cxTokenCount = 0;
clang_tokenize(m_cxTranslationUnit, cursor.cxSourceRange(), &cxTokens, &cxTokenCount);
Utf8String aliasedName;
// Start at 3 in order to skip these tokens: namespace X =
for (uint i = 3; i < cxTokenCount; ++i)
aliasedName += ClangString(clang_getTokenSpelling(m_cxTranslationUnit, cxTokens[i]));
clang_disposeTokens(m_cxTranslationUnit, cxTokens, cxTokenCount);
return aliasedName;
}
static Utf8String typeName(const Type &type)
{
return type.declaration().spelling();
}
static Utf8String qdocMark(const Cursor &cursor)
{
if (cursor.kind() == CXCursor_ClassTemplate)
return cursor.spelling();
if (cursor.type().kind() == CXType_Enum
|| cursor.type().kind() == CXType_Typedef
|| cursor.type().kind() == CXType_Record)
return typeName(cursor.type());
Utf8String text = cursor.displayName();
if (cursor.kind() == CXCursor_FunctionDecl) {
// TODO: Remove this workaround by fixing this in
// libclang with the help of CXPrintingPolicy.
text.replace(Utf8StringLiteral("<>"), Utf8String());
}
return text;
}
static ToolTipInfo::QdocCategory qdocCategory(const Cursor &cursor)
{
if (cursor.isFunctionLike())
return ToolTipInfo::Function;
if (cursor.kind() == CXCursor_MacroDefinition)
return ToolTipInfo::Macro;
if (cursor.kind() == CXCursor_EnumConstantDecl)
return ToolTipInfo::Enum;
if (cursor.type().kind() == CXType_Enum)
return ToolTipInfo::Enum;
if (cursor.kind() == CXCursor_InclusionDirective)
return ToolTipInfo::Brief;
// TODO: Handle CXCursor_NamespaceAlias, too?!
if (cursor.kind() == CXCursor_Namespace)
return ToolTipInfo::ClassOrNamespace;
if (cursor.isCompoundType())
return ToolTipInfo::ClassOrNamespace;
if (cursor.kind() == CXCursor_NamespaceAlias)
return ToolTipInfo::ClassOrNamespace;
if (cursor.type().kind() == CXType_Typedef)
return ToolTipInfo::Typedef;
if (cursor.type().kind() == CXType_Record)
return ToolTipInfo::ClassOrNamespace;
if (cursor.kind() == CXCursor_TypeAliasTemplateDecl)
return ToolTipInfo::Typedef;
if (cursor.kind() == CXCursor_ClassTemplate)
return ToolTipInfo::ClassOrNamespace;
return ToolTipInfo::Unknown;
}
static Utf8String name(const Cursor &cursor)
{
if (cursor.type().kind() == CXType_Record || cursor.kind() == CXCursor_EnumDecl)
return typeName(cursor.type());
return cursor.spelling();
}
static Utf8StringVector qDocIdCandidates(const Cursor &cursor)
{
Utf8StringVector components = qualificationPrefixAsVector(cursor);
if (components.isEmpty())
return { name(cursor) };
components << name(cursor);
Utf8StringVector result;
Utf8String name;
for (auto it = components.rbegin(); it != components.rend(); ++it) {
if (name.isEmpty())
name = *it;
else
name = *it + (Utf8StringLiteral("::") + name);
result.prepend(name);
}
return result;
}
// TODO: Add libclang API for this?!
static bool isBuiltinOrPointerToBuiltin(const Type &type)
{
Type theType = type;
if (theType.isBuiltinType())
return true;
// TODO: Simplify
// TODO: Test with **
while (theType.pointeeType().isValid()) {
theType = theType.pointeeType();
if (theType.isBuiltinType())
return true;
}
return false;
}
ToolTipInfo ToolTipInfoCollector::qDocInfo(const Cursor &cursor) const
{
ToolTipInfo result;
if (isBuiltinOrPointerToBuiltin(cursor.type()))
return result;
result.setQdocIdCandidates(qDocIdCandidates(cursor));
result.setQdocMark(qdocMark(cursor));
result.setQdocCategory(qdocCategory(cursor));
if (cursor.type().kind() == CXType_Record) {
result.setQdocIdCandidates(qDocIdCandidates(cursor.type().declaration()));
return result;
}
if (cursor.kind() == CXCursor_VarDecl || cursor.kind() == CXCursor_FieldDecl) {
result.setQdocMark(typeName(cursor.type()));
// maybe template instantiation
if (cursor.type().kind() == CXType_Unexposed && cursor.type().canonical().kind() == CXType_Record) {
result.setQdocIdCandidates(qDocIdCandidates(cursor.type().canonical().declaration()));
result.setQdocCategory(ToolTipInfo::ClassOrNamespace);
return result;
}
}
// TODO: Handle also RValueReference()
if (cursor.type().isLValueReference()) {
const Cursor pointeeTypeDeclaration = cursor.type().pointeeType().declaration();
result.setQdocIdCandidates(qDocIdCandidates(pointeeTypeDeclaration));
result.setQdocMark(pointeeTypeDeclaration.spelling());
result.setQdocCategory(qdocCategory(pointeeTypeDeclaration));
return result;
}
return result;
}
CXSourceLocation ToolTipInfoCollector::toCXSourceLocation(uint line, uint column) const
{
return clang_getLocation(m_cxTranslationUnit,
clang_getFile(m_cxTranslationUnit,
m_mainFilePath.constData()),
line,
column);
}
UnsavedFile ToolTipInfoCollector::unsavedFile(const Utf8String &filePath) const
{
const UnsavedFile &unsavedFile = m_unsavedFiles.unsavedFile(filePath);
if (!unsavedFile.filePath().isEmpty())
return unsavedFile;
// Create an unsaved file with the file content from disk.
// TODO: Make use of clang_getFileContents() instead of reading from disk.
QTextCodec *codec = QTextCodec::codecForName(m_textCodecName);
QByteArray fileContent;
QString errorString;
using namespace Utils;
const TextFileFormat::ReadResult readResult
= TextFileFormat::readFileUTF8(filePath.toString(), codec, &fileContent, &errorString);
if (readResult != TextFileFormat::ReadSuccess) {
qWarning() << "Failed to read file" << filePath << ":" << errorString;
return UnsavedFile();
}
return UnsavedFile(filePath, Utf8String::fromByteArray(fileContent));
}
Utf8String ToolTipInfoCollector::lineRange(const Utf8String &filePath,
unsigned fromLine,
unsigned toLine) const
{
if (toLine < fromLine)
return Utf8String();
const UnsavedFile file = unsavedFile(filePath);
if (file.fileContent().isEmpty())
return Utf8String();
return file.lineRange(fromLine, toLine);
}
ToolTipInfo ToolTipInfoCollector::collect(uint line, uint column) const
{
const Cursor cursor = clang_getCursor(m_cxTranslationUnit, toCXSourceLocation(line, column));
if (!cursor.isValid())
return ToolTipInfo(); // E.g. cursor on ifdeffed out range
const Cursor referenced = referencedCursor(cursor);
QTC_CHECK(referenced.isValid());
ToolTipInfo info;
info.setText(text(cursor, referenced));
info.setBriefComment(referenced.briefComment());
{
ToolTipInfo qDocToolTipInfo = qDocInfo(referenced);
info.setQdocIdCandidates(qDocToolTipInfo.qdocIdCandidates());
info.setQdocMark(qDocToolTipInfo.qdocMark());
info.setQdocCategory(qDocToolTipInfo.qdocCategory());
}
info.setSizeInBytes(sizeInBytes(cursor));
return info;
}
} // anonymous namespace
ToolTipInfo collectToolTipInfo(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
const Utf8String &mainFilePath,
CXTranslationUnit cxTranslationUnit,
uint line,
uint column)
{
ToolTipInfoCollector collector(unsavedFiles, textCodecName, mainFilePath, cxTranslationUnit);
const ToolTipInfo info = collector.collect(line, column);
return info;
}
} // namespace ClangBackEnd

View File

@@ -0,0 +1,45 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#pragma once
#include <utf8string.h>
#include <clangsupport/tooltipinfo.h>
#include <clang-c/Index.h>
namespace ClangBackEnd {
class UnsavedFiles;
ToolTipInfo collectToolTipInfo(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
const Utf8String &mainFilePath,
CXTranslationUnit cxTranslationUnit,
uint line,
uint column);
} // namespace ClangBackEnd

View File

@@ -27,6 +27,7 @@
#include "clangbackend_global.h" #include "clangbackend_global.h"
#include "clangreferencescollector.h" #include "clangreferencescollector.h"
#include "clangtooltipinfocollector.h"
#include "clangtranslationunitupdater.h" #include "clangtranslationunitupdater.h"
#include "clangfollowsymbol.h" #include "clangfollowsymbol.h"
#include "clangfollowsymboljob.h" #include "clangfollowsymboljob.h"
@@ -139,6 +140,20 @@ void TranslationUnit::extractDocumentAnnotations(
skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers(); skippedSourceRanges = this->skippedSourceRanges().toSourceRangeContainers();
} }
ToolTipInfo TranslationUnit::tooltip(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
uint line,
uint column) const
{
return collectToolTipInfo(unsavedFiles,
textCodecName,
filePath(),
m_cxTranslationUnit,
line,
column);
}
ReferencesResult TranslationUnit::references(uint line, uint column, bool localReferences) const ReferencesResult TranslationUnit::references(uint line, uint column, bool localReferences) const
{ {
return collectReferences(m_cxTranslationUnit, line, column, localReferences); return collectReferences(m_cxTranslationUnit, line, column, localReferences);

View File

@@ -41,6 +41,7 @@ class SkippedSourceRanges;
class SourceLocation; class SourceLocation;
class SourceRange; class SourceRange;
class SourceRangeContainer; class SourceRangeContainer;
class ToolTipInfo;
class TranslationUnitUpdateInput; class TranslationUnitUpdateInput;
class TranslationUnitUpdateResult; class TranslationUnitUpdateResult;
class UnsavedFiles; class UnsavedFiles;
@@ -86,6 +87,10 @@ public:
ReferencesResult references(uint line, uint column, bool localReferences = false) const; ReferencesResult references(uint line, uint column, bool localReferences = false) const;
ToolTipInfo tooltip(UnsavedFiles &unsavedFiles,
const Utf8String &textCodecName,
uint line,
uint column) const;
DiagnosticSet diagnostics() const; DiagnosticSet diagnostics() const;
SourceLocation sourceLocationAt(uint line, uint column) const; SourceLocation sourceLocationAt(uint line, uint column) const;

View File

@@ -84,6 +84,16 @@ bool Type::isBuiltinType() const
return cxType.kind >= CXType_FirstBuiltin && cxType.kind <= CXType_LastBuiltin; return cxType.kind >= CXType_FirstBuiltin && cxType.kind <= CXType_LastBuiltin;
} }
bool Type::isUnsigned() const
{
return cxType.kind == CXType_UChar
|| cxType.kind == CXType_UShort
|| cxType.kind == CXType_UInt
|| cxType.kind == CXType_ULong
|| cxType.kind == CXType_ULongLong
|| cxType.kind == CXType_UInt128;
}
Utf8String Type::utf8Spelling() const Utf8String Type::utf8Spelling() const
{ {
return ClangString(clang_getTypeSpelling(cxType)); return ClangString(clang_getTypeSpelling(cxType));
@@ -94,6 +104,83 @@ ClangString Type::spelling() const
return ClangString(clang_getTypeSpelling(cxType)); return ClangString(clang_getTypeSpelling(cxType));
} }
static const char *builtinTypeToText(CXTypeKind kind)
{
// CLANG-UPGRADE-CHECK: Check for added built-in types.
switch (kind) {
case CXType_Void:
return "void";
case CXType_Bool:
return "bool";
// See also ${CLANG_REPOSITORY}/lib/Sema/SemaChecking.cpp - IsSameCharType().
case CXType_Char_U:
case CXType_UChar:
return "unsigned char";
case CXType_Char_S:
case CXType_SChar:
return "signed char";
case CXType_Char16:
return "char16_t";
case CXType_Char32:
return "char32_t";
case CXType_WChar:
return "wchar_t";
case CXType_UShort:
return "unsigned short";
case CXType_UInt:
return "unsigned int";
case CXType_ULong:
return "unsigned long";
case CXType_ULongLong:
return "unsigned long long";
case CXType_Short:
return "short";
case CXType_Int:
return "int";
case CXType_Long:
return "long";
case CXType_LongLong:
return "long long";
case CXType_Float:
return "float";
case CXType_Double:
return "double";
case CXType_LongDouble:
return "long double";
case CXType_NullPtr:
return "nullptr_t";
// https://gcc.gnu.org/onlinedocs/gcc/_005f_005fint128.html
case CXType_Int128: return "__int128";
case CXType_UInt128: return "unsigned __int128";
// https://gcc.gnu.org/onlinedocs/gcc/Floating-Types.html
case CXType_Float128: return "__float128";
// CLANG-UPGRADE-CHECK: CXType_Float16 available with >= clang-6.0:
// case CXType_Float16: return "_Float16";
// https://www.khronos.org/registry/OpenCL/sdk/2.1/docs/man/xhtml/scalarDataTypes.html
case CXType_Half:
return "half";
default:
return "";
}
}
Utf8String Type::builtinTypeToString() const
{
const char *text = builtinTypeToText(cxType.kind);
return Utf8String::fromByteArray(QByteArray::fromRawData(text, strlen(text)));
}
int Type::argumentCount() const int Type::argumentCount() const
{ {
return clang_getNumArgTypes(cxType); return clang_getNumArgTypes(cxType);
@@ -129,6 +216,16 @@ Cursor Type::declaration() const
return clang_getTypeDeclaration(cxType); return clang_getTypeDeclaration(cxType);
} }
long long Type::sizeOf(bool *isValid) const
{
const long long size = clang_Type_getSizeOf(cxType);
*isValid = size != CXTypeLayoutError_Invalid
&& size != CXTypeLayoutError_Incomplete
&& size != CXTypeLayoutError_Dependent;
return size;
}
CXTypeKind Type::kind() const CXTypeKind Type::kind() const
{ {
return cxType.kind; return cxType.kind;

View File

@@ -53,9 +53,11 @@ public:
bool isReferencingConstant() const; bool isReferencingConstant() const;
bool isOutputArgument() const; bool isOutputArgument() const;
bool isBuiltinType() const; bool isBuiltinType() const;
bool isUnsigned() const;
Utf8String utf8Spelling() const; Utf8String utf8Spelling() const;
ClangString spelling() const; ClangString spelling() const;
Utf8String builtinTypeToString() const;
int argumentCount() const; int argumentCount() const;
Type alias() const; Type alias() const;
@@ -66,6 +68,8 @@ public:
Cursor declaration() const; Cursor declaration() const;
long long sizeOf(bool *isValid) const;
CXTypeKind kind() const; CXTypeKind kind() const;
private: private:

View File

@@ -158,6 +158,14 @@ bool Cursor::isTemplateLike() const
Q_UNREACHABLE(); Q_UNREACHABLE();
} }
bool Cursor::isAnyTypeAlias() const
{
const CXCursorKind k = kind();
return k == CXCursor_TypeAliasDecl
|| k == CXCursor_TypedefDecl
|| k == CXCursor_TypeAliasTemplateDecl;
}
bool Cursor::hasFinalFunctionAttribute() const bool Cursor::hasFinalFunctionAttribute() const
{ {
bool hasFinal = false; bool hasFinal = false;
@@ -248,11 +256,31 @@ Type Cursor::nonPointerTupe() const
return typeResult; return typeResult;
} }
Type Cursor::enumType() const
{
return clang_getEnumDeclIntegerType(cxCursor);
}
long long Cursor::enumConstantValue() const
{
return clang_getEnumConstantDeclValue(cxCursor);
}
unsigned long long Cursor::enumConstantUnsignedValue() const
{
return clang_getEnumConstantDeclUnsignedValue(cxCursor);
}
Cursor Cursor::specializedCursorTemplate() const Cursor Cursor::specializedCursorTemplate() const
{ {
return clang_getSpecializedCursorTemplate(cxCursor); return clang_getSpecializedCursorTemplate(cxCursor);
} }
CXFile Cursor::includedFile() const
{
return clang_getIncludedFile(cxCursor);
}
SourceLocation Cursor::sourceLocation() const SourceLocation Cursor::sourceLocation() const
{ {
return clang_getCursorLocation(cxCursor); return clang_getCursorLocation(cxCursor);
@@ -341,6 +369,11 @@ Cursor Cursor::functionBase() const
return functionBaseCursor; return functionBaseCursor;
} }
Type Cursor::resultType() const
{
return clang_getResultType(type().cxType);
}
Cursor Cursor::argument(int index) const Cursor Cursor::argument(int index) const
{ {
return clang_Cursor_getArgument(cxCursor, index); return clang_Cursor_getArgument(cxCursor, index);
@@ -398,6 +431,11 @@ CXCursorKind Cursor::kind() const
return clang_getCursorKind(cxCursor); return clang_getCursorKind(cxCursor);
} }
CXCursor Cursor::cx() const
{
return cxCursor;
}
bool operator==(const Cursor &first, const Cursor &second) bool operator==(const Cursor &first, const Cursor &second)
{ {
return clang_equalCursors(first.cxCursor, second.cxCursor); return clang_equalCursors(first.cxCursor, second.cxCursor);

View File

@@ -67,6 +67,7 @@ public:
bool isFunctionLike() const; bool isFunctionLike() const;
bool isConstructorOrDestructor() const; bool isConstructorOrDestructor() const;
bool isTemplateLike() const; bool isTemplateLike() const;
bool isAnyTypeAlias() const;
bool hasFinalFunctionAttribute() const; bool hasFinalFunctionAttribute() const;
bool hasFinalClassAttribute() const; bool hasFinalClassAttribute() const;
bool isUnexposed() const; bool isUnexposed() const;
@@ -81,6 +82,10 @@ public:
Type type() const; Type type() const;
Type nonPointerTupe() const; Type nonPointerTupe() const;
Type enumType() const;
long long enumConstantValue() const;
unsigned long long enumConstantUnsignedValue() const;
SourceLocation sourceLocation() const; SourceLocation sourceLocation() const;
CXSourceLocation cxSourceLocation() const; CXSourceLocation cxSourceLocation() const;
@@ -92,17 +97,19 @@ public:
Cursor definition() const; Cursor definition() const;
Cursor canonical() const; Cursor canonical() const;
Cursor alias() const;
Cursor referenced() const; Cursor referenced() const;
Cursor semanticParent() const; Cursor semanticParent() const;
Cursor lexicalParent() const; Cursor lexicalParent() const;
Cursor functionBaseDeclaration() const; Cursor functionBaseDeclaration() const;
Cursor functionBase() const; Cursor functionBase() const;
Type resultType() const;
Cursor argument(int index) const; Cursor argument(int index) const;
unsigned overloadedDeclarationsCount() const; unsigned overloadedDeclarationsCount() const;
Cursor overloadedDeclaration(unsigned index) const; Cursor overloadedDeclaration(unsigned index) const;
Cursor specializedCursorTemplate() const; Cursor specializedCursorTemplate() const;
CXFile includedFile() const;
void collectOutputArgumentRangesTo( void collectOutputArgumentRangesTo(
std::vector<CXSourceRange> &outputArgumentRanges) const; std::vector<CXSourceRange> &outputArgumentRanges) const;
std::vector<CXSourceRange> outputArgumentRanges() const; std::vector<CXSourceRange> outputArgumentRanges() const;
@@ -112,6 +119,8 @@ public:
template <class VisitorCallback> template <class VisitorCallback>
void visit(VisitorCallback visitorCallback) const; void visit(VisitorCallback visitorCallback) const;
CXCursor cx() const;
private: private:
CXCursor cxCursor; CXCursor cxCursor;
}; };

View File

@@ -30,6 +30,8 @@
#include <ostream> #include <ostream>
#include <utils/qtcassert.h>
namespace ClangBackEnd { namespace ClangBackEnd {
UnsavedFile::UnsavedFile() UnsavedFile::UnsavedFile()
@@ -79,6 +81,25 @@ bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
return positionIsOk && hasCharacterAt(utf8Position, character); return positionIsOk && hasCharacterAt(utf8Position, character);
} }
Utf8String UnsavedFile::lineRange(uint fromLine, uint toLine) const
{
QTC_ASSERT(fromLine <= toLine, return Utf8String());
// Find start of first line
bool ok = false;
const uint fromPosition = toUtf8Position(fromLine, 1, &ok);
QTC_ASSERT(ok, return Utf8String());
// Find end of last line
uint toPosition = toUtf8Position(toLine, 1, &ok);
QTC_ASSERT(ok, return Utf8String());
const uint endPosition = uint(m_fileContent.byteSize());
while (toPosition < endPosition && m_fileContent.constData()[toPosition] != '\n')
++toPosition;
return m_fileContent.mid(int(fromPosition), int(toPosition - fromPosition));
}
bool UnsavedFile::hasCharacterAt(uint position, char character) const bool UnsavedFile::hasCharacterAt(uint position, char character) const
{ {
if (position < uint(m_fileContent.byteSize())) if (position < uint(m_fileContent.byteSize()))

View File

@@ -46,6 +46,7 @@ public:
// 1-based line and column // 1-based line and column
uint toUtf8Position(uint line, uint column, bool *ok) const; uint toUtf8Position(uint line, uint column, bool *ok) const;
bool hasCharacterAt(uint line, uint column, char character) const; bool hasCharacterAt(uint line, uint column, char character) const;
Utf8String lineRange(uint fromLine, uint toLine) const;
// 0-based position // 0-based position
bool hasCharacterAt(uint position, char character) const; bool hasCharacterAt(uint position, char character) const;

View File

@@ -98,6 +98,11 @@ void EchoClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMess
echoMessage(message); echoMessage(message);
} }
void EchoClangCodeModelServer::requestToolTip(const RequestToolTipMessage &message)
{
echoMessage(message);
}
void EchoClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) void EchoClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{ {
echoMessage(message); echoMessage(message);

View File

@@ -47,6 +47,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override; void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override; void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override; void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
void requestToolTip(const RequestToolTipMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override; void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
private: private:

View File

@@ -0,0 +1,628 @@
/****************************************************************************
**
** Copyright (C) 2018 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/
#include "googletest.h"
#include "rundocumentparse-utility.h"
#include <clangdocument.h>
#include <clangdocuments.h>
#include <clangsupport_global.h>
#include <clangtooltipinfocollector.h>
#include <clangtranslationunit.h>
#include <fixitcontainer.h>
#include <projectpart.h>
#include <projects.h>
#include <sourcelocationcontainer.h>
#include <sourcerangecontainer.h>
#include <unsavedfiles.h>
#include <utils/qtcassert.h>
#include <clang-c/Index.h>
using ::ClangBackEnd::ProjectPart;
using ::ClangBackEnd::SourceLocationContainer;
using ::ClangBackEnd::Document;
using ::ClangBackEnd::UnsavedFiles;
using ::ClangBackEnd::ToolTipInfo;
using ::ClangBackEnd::SourceRangeContainer;
namespace {
#define CHECK_MEMBER(actual, expected, memberName) \
if (actual.memberName() != expected.memberName()) { \
*result_listener << #memberName " is " + PrintToString(actual.memberName()) \
<< " and not " + PrintToString(expected.memberName()); \
return false; \
}
MATCHER_P(IsToolTip, expected, std::string(negation ? "isn't" : "is") + PrintToString(expected))
{
CHECK_MEMBER(arg, expected, text);
CHECK_MEMBER(arg, expected, briefComment);
CHECK_MEMBER(arg, expected, qdocIdCandidates);
CHECK_MEMBER(arg, expected, qdocMark);
CHECK_MEMBER(arg, expected, qdocCategory);
CHECK_MEMBER(arg, expected, sizeInBytes);
return true;
}
MATCHER_P(IsQdocToolTip, expected, std::string(negation ? "isn't" : "is") + PrintToString(expected))
{
CHECK_MEMBER(arg, expected, qdocIdCandidates);
CHECK_MEMBER(arg, expected, qdocMark);
CHECK_MEMBER(arg, expected, qdocCategory);
return true;
}
#undef CHECK_MEMBER
struct Data {
ProjectPart projectPart{Utf8StringLiteral("projectPartId"), {Utf8StringLiteral("-std=c++14")}};
ClangBackEnd::ProjectParts projects;
ClangBackEnd::UnsavedFiles unsavedFiles;
ClangBackEnd::Documents documents{projects, unsavedFiles};
Document document{Utf8StringLiteral(TESTDATA_DIR "/tooltipinfo.cpp"),
projectPart,
{},
documents};
UnitTest::RunDocumentParse _1{document};
};
class ToolTipInfo : public ::testing::Test
{
protected:
::ToolTipInfo tooltip(uint line, uint column)
{
return d->document.translationUnit().tooltip(d->unsavedFiles,
Utf8StringLiteral("UTF-8"),
line,
column);
}
static void SetUpTestCase();
static void TearDownTestCase();
private:
static std::unique_ptr<Data> d;
};
TEST_F(ToolTipInfo, LocalVariableInt)
{
const ::ToolTipInfo actual = tooltip(3, 5);
ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("int"))));
}
TEST_F(ToolTipInfo, LocalVariablePointerToConstInt)
{
const ::ToolTipInfo actual = tooltip(4, 5);
ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("const int *"))));
}
TEST_F(ToolTipInfo, LocalParameterVariableConstRefCustomType)
{
::ToolTipInfo expected(Utf8StringLiteral("const Foo &"));
expected.setQdocIdCandidates({Utf8StringLiteral("Foo")});
expected.setQdocMark(Utf8StringLiteral("Foo"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(12, 12);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, LocalNonParameterVariableConstRefCustomType)
{
::ToolTipInfo expected(Utf8StringLiteral("const Foo"));
expected.setQdocIdCandidates({Utf8StringLiteral("Foo")});
expected.setQdocMark(Utf8StringLiteral("Foo"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(14, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, MemberVariable)
{
const ::ToolTipInfo actual = tooltip(12, 16);
ASSERT_THAT(actual, IsToolTip(::ToolTipInfo(Utf8StringLiteral("int"))));
}
TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(MemberFunctionCall_QualifiedName))
{
const ::ToolTipInfo actual = tooltip(21, 9);
ASSERT_THAT(actual.text(), Utf8StringLiteral("int Bar::mem()"));
}
// ChangeLog: Show extra specifiers. For functions e.g.: virtual, inline, explicit, const, volatile
TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(MemberFunctionCall_ExtraSpecifiers))
{
const ::ToolTipInfo actual = tooltip(22, 9);
ASSERT_THAT(actual.text(), Utf8StringLiteral("virtual int Bar::virtualConstMem() const"));
}
TEST_F(ToolTipInfo, MemberFunctionCall_qdocIdCandidates)
{
const ::ToolTipInfo actual = tooltip(21, 9);
ASSERT_THAT(actual.qdocIdCandidates(), ElementsAre(Utf8StringLiteral("Bar::mem"),
Utf8StringLiteral("mem")));
}
TEST_F(ToolTipInfo, MemberFunctionCall_qdocMark_FIXLIBCLANG_CHECKED)
{
const ::ToolTipInfo actual = tooltip(21, 9);
ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("mem()"));
}
// TODO: Check what is really needed for qdoc before implementing this one.
TEST_F(ToolTipInfo, DISABLED_MemberFunctionCall_qdocMark_extraSpecifiers)
{
const ::ToolTipInfo actual = tooltip(22, 9);
ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("virtualConstMem() const"));
}
TEST_F(ToolTipInfo, MemberFunctionCall_qdocCategory)
{
const ::ToolTipInfo actual = tooltip(21, 9);
ASSERT_THAT(actual.qdocCategory(), ::ToolTipInfo::Function);
}
// TODO: Show the template parameter type, too: "template<typename T>...)"
TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(TemplateFunctionCall))
{
const ::ToolTipInfo actual = tooltip(30, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("template<> void t<Foo>(int foo)"));
}
TEST_F(ToolTipInfo, TemplateFunctionCall_qdocIdCandidates)
{
const ::ToolTipInfo actual = tooltip(30, 5);
ASSERT_THAT(actual.qdocIdCandidates(), ElementsAre(Utf8StringLiteral("t")));
}
TEST_F(ToolTipInfo, TemplateFunctionCall_qdocMark_FIXLIBCLANG_CHECKED)
{
const ::ToolTipInfo actual = tooltip(30, 5);
ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("t(int)"));
}
TEST_F(ToolTipInfo, TemplateFunctionCall_qdocCategory)
{
const ::ToolTipInfo actual = tooltip(30, 5);
ASSERT_THAT(actual.qdocCategory(), ::ToolTipInfo::Function);
}
TEST_F(ToolTipInfo, BriefComment)
{
const ::ToolTipInfo actual = tooltip(41, 5);
ASSERT_THAT(actual.briefComment(), Utf8StringLiteral("This is a crazy function."));
}
TEST_F(ToolTipInfo, Enum)
{
::ToolTipInfo expected(Utf8StringLiteral("EnumType"));
expected.setQdocIdCandidates({Utf8StringLiteral("EnumType")});
expected.setQdocMark(Utf8StringLiteral("EnumType"));
expected.setQdocCategory(::ToolTipInfo::Enum);
const ::ToolTipInfo actual = tooltip(49, 12);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, Enumerator)
{
::ToolTipInfo expected(Utf8StringLiteral("6"));
expected.setQdocIdCandidates({Utf8StringLiteral("Custom")});
expected.setQdocMark(Utf8StringLiteral("EnumType"));
expected.setQdocCategory(::ToolTipInfo::Enum);
const ::ToolTipInfo actual = tooltip(49, 22);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, TemplateTypeFromParameter)
{
::ToolTipInfo expected(Utf8StringLiteral("const Baz<int> &"));
expected.setQdocIdCandidates({Utf8StringLiteral("Baz")});
expected.setQdocMark(Utf8StringLiteral("Baz"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(55, 25);
ASSERT_THAT(actual, IsQdocToolTip(expected));
}
TEST_F(ToolTipInfo, TemplateTypeFromNonParameter)
{
::ToolTipInfo expected(Utf8StringLiteral("Baz<int>"));
expected.setQdocIdCandidates({Utf8StringLiteral("Baz")});
expected.setQdocMark(Utf8StringLiteral("Baz"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(56, 19);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, IncludeDirective)
{
::ToolTipInfo expected(Utf8StringLiteral(TESTDATA_DIR"/tooltipinfo.h"));
expected.setQdocIdCandidates({Utf8StringLiteral("tooltipinfo.h")});
expected.setQdocMark(Utf8StringLiteral("tooltipinfo.h"));
expected.setQdocCategory(::ToolTipInfo::Brief);
const ::ToolTipInfo actual = tooltip(59, 11);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, MacroUse_WithMacroFromSameFile)
{
const ::ToolTipInfo actual = tooltip(66, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("#define MACRO_FROM_MAINFILE(x) x + 3"));
}
TEST_F(ToolTipInfo, MacroUse_WithMacroFromHeader)
{
const ::ToolTipInfo actual = tooltip(67, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("#define MACRO_FROM_HEADER(x) x + \\\n x + \\\n x"));
}
TEST_F(ToolTipInfo, MacroUse_qdoc)
{
::ToolTipInfo expected;
expected.setQdocIdCandidates({Utf8StringLiteral("MACRO_FROM_MAINFILE")});
expected.setQdocMark(Utf8StringLiteral("MACRO_FROM_MAINFILE"));
expected.setQdocCategory(::ToolTipInfo::Macro);
const ::ToolTipInfo actual = tooltip(66, 5);
ASSERT_THAT(actual, IsQdocToolTip(expected));
}
TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDirectiveIsQualified)
{
::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
expected.setQdocMark(Utf8StringLiteral("Muu"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(77, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDirectiveOfAliasIsResolvedAndQualified)
{
::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
expected.setQdocMark(Utf8StringLiteral("Muu"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(82, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, TypeNameIntroducedByUsingDeclarationIsQualified)
{
::ToolTipInfo expected(Utf8StringLiteral("N::Muu"));
expected.setQdocIdCandidates({Utf8StringLiteral("N::Muu"), Utf8StringLiteral("Muu")});
expected.setQdocMark(Utf8StringLiteral("Muu"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(87, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, SizeForClassDefinition)
{
const ::ToolTipInfo actual = tooltip(92, 8);
ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("2"));
}
TEST_F(ToolTipInfo, SizeForMemberField)
{
const ::ToolTipInfo actual = tooltip(95, 10);
ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("1"));
}
TEST_F(ToolTipInfo, SizeForEnum)
{
const ::ToolTipInfo actual = tooltip(97, 12);
ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("4"));
}
TEST_F(ToolTipInfo, SizeForUnion)
{
const ::ToolTipInfo actual = tooltip(98, 7);
ASSERT_THAT(actual.sizeInBytes(), Utf8StringLiteral("1"));
}
TEST_F(ToolTipInfo, Namespace)
{
::ToolTipInfo expected(Utf8StringLiteral("X"));
expected.setQdocIdCandidates({Utf8StringLiteral("X")});
expected.setQdocMark(Utf8StringLiteral("X"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(106, 11);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, NamespaceQualified)
{
::ToolTipInfo expected(Utf8StringLiteral("X::Y"));
expected.setQdocIdCandidates({Utf8StringLiteral("X::Y"), Utf8StringLiteral("Y")});
expected.setQdocMark(Utf8StringLiteral("Y"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(107, 11);
ASSERT_THAT(actual, IsToolTip(expected));
}
// TODO: Show unresolved and resolved name, for F1 try both.
TEST_F(ToolTipInfo, TypeName_ResolveTypeDef)
{
::ToolTipInfo expected(Utf8StringLiteral("Ptr<Nuu>"));
expected.setQdocIdCandidates({Utf8StringLiteral("PtrFromTypeDef")});
expected.setQdocMark(Utf8StringLiteral("PtrFromTypeDef"));
expected.setQdocCategory(::ToolTipInfo::Typedef);
const ::ToolTipInfo actual = tooltip(122, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
// TODO: Show unresolved and resolved name, for F1 try both.
TEST_F(ToolTipInfo, TypeName_ResolveAlias)
{
::ToolTipInfo expected(Utf8StringLiteral("Ptr<Nuu>"));
expected.setQdocIdCandidates({Utf8StringLiteral("PtrFromTypeAlias")});
expected.setQdocMark(Utf8StringLiteral("PtrFromTypeAlias"));
expected.setQdocCategory(::ToolTipInfo::Typedef);
const ::ToolTipInfo actual = tooltip(123, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
// The referenced cursor is a CXCursor_TypeAliasTemplateDecl, its type is invalid
// and so probably clang_getTypedefDeclUnderlyingType() does not return anything useful.
// TODO: Fix the cursor's type or add new API in libclang for querying the template type alias.
TEST_F(ToolTipInfo, DISABLED_TypeName_ResolveTemplateTypeAlias)
{
const ::ToolTipInfo actual = tooltip(124, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("Ptr<Nuu>"));
}
TEST_F(ToolTipInfo, TypeName_ResolveTemplateTypeAlias_qdoc)
{
::ToolTipInfo expected;
expected.setQdocIdCandidates({Utf8StringLiteral("PtrFromTemplateTypeAlias")});
expected.setQdocMark(Utf8StringLiteral("PtrFromTemplateTypeAlias"));
expected.setQdocCategory(::ToolTipInfo::Typedef);
const ::ToolTipInfo actual = tooltip(124, 5);
ASSERT_THAT(actual, IsQdocToolTip(expected));
}
TEST_F(ToolTipInfo, TemplateClassReference)
{
::ToolTipInfo expected(Utf8StringLiteral("Zii<T>"));
expected.setQdocIdCandidates({Utf8StringLiteral("Zii")});
expected.setQdocMark(Utf8StringLiteral("Zii"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(134, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, TemplateClassQualified)
{
::ToolTipInfo expected(Utf8StringLiteral("U::Yii<T>"));
expected.setQdocIdCandidates({Utf8StringLiteral("U::Yii"), Utf8StringLiteral("Yii")});
expected.setQdocMark(Utf8StringLiteral("Yii"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(135, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, ResolveNamespaceAliasForType)
{
::ToolTipInfo expected(Utf8StringLiteral("A::X"));
expected.setQdocIdCandidates({Utf8StringLiteral("A::X"), Utf8StringLiteral("X")});
expected.setQdocMark(Utf8StringLiteral("X"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(144, 8);
ASSERT_THAT(actual, IsToolTip(expected));
}
// TODO: Show unresolved and resolved name, for F1 try both.
TEST_F(ToolTipInfo, ResolveNamespaceAlias)
{
::ToolTipInfo expected(Utf8StringLiteral("A"));
expected.setQdocIdCandidates({Utf8StringLiteral("B")});
expected.setQdocMark(Utf8StringLiteral("B"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
const ::ToolTipInfo actual = tooltip(144, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, QualificationForTemplateClassInClassInNamespace)
{
::ToolTipInfo expected(Utf8StringLiteral("N::Outer::Inner<int>"));
expected.setQdocIdCandidates({Utf8StringLiteral("N::Outer::Inner"),
Utf8StringLiteral("Outer::Inner"),
Utf8StringLiteral("Inner")});
expected.setQdocMark(Utf8StringLiteral("Inner"));
expected.setQdocCategory(::ToolTipInfo::ClassOrNamespace);
expected.setSizeInBytes(Utf8StringLiteral("1"));
const ::ToolTipInfo actual = tooltip(153, 16);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, Function)
{
::ToolTipInfo expected(Utf8StringLiteral("void f()"));
expected.setQdocIdCandidates({Utf8StringLiteral("f")});
expected.setQdocMark(Utf8StringLiteral("f()"));
expected.setQdocCategory(::ToolTipInfo::Function);
const ::ToolTipInfo actual = tooltip(165, 5);
ASSERT_THAT(actual, IsToolTip(expected));
}
TEST_F(ToolTipInfo, Function_QualifiedName)
{
const ::ToolTipInfo actual = tooltip(166, 8);
ASSERT_THAT(actual.text(), Utf8StringLiteral("void R::f()"));
}
TEST_F(ToolTipInfo, Function_qdocIdCandidatesAreQualified)
{
const ::ToolTipInfo actual = tooltip(166, 8);
ASSERT_THAT(actual.qdocIdCandidates(), ElementsAre(Utf8StringLiteral("R::f"),
Utf8StringLiteral("f")));
}
TEST_F(ToolTipInfo, DISABLED_WITHOUT_PRETTYDECL_PATCH(Function_HasParameterName))
{
const ::ToolTipInfo actual = tooltip(167, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("void f(int param)"));
}
// TODO: Implement with CXPrintingPolicy
TEST_F(ToolTipInfo, DISABLED_Function_HasDefaultArgument)
{
const ::ToolTipInfo actual = tooltip(168, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("void z(int = 1)"));
}
TEST_F(ToolTipInfo, Function_qdocMarkHasNoParameterName)
{
const ::ToolTipInfo actual = tooltip(167, 5);
ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("f(int)"));
}
TEST_F(ToolTipInfo, Function_qdocMarkHasNoDefaultArgument)
{
const ::ToolTipInfo actual = tooltip(168, 5);
ASSERT_THAT(actual.qdocMark(), Utf8StringLiteral("z(int)"));
}
TEST_F(ToolTipInfo, AutoTypeBuiltin)
{
const ::ToolTipInfo actual = tooltip(176, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("int"));
}
// TODO: Test for qdoc entries, too.
TEST_F(ToolTipInfo, AutoTypeEnum)
{
const ::ToolTipInfo actual = tooltip(177, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("EnumType"));
}
// TODO: Test for qdoc entries, too.
TEST_F(ToolTipInfo, AutoTypeClassType)
{
const ::ToolTipInfo actual = tooltip(178, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("Bar"));
}
// TODO: Test for qdoc entries, too.
// TODO: Deduced template arguments work, too?!
TEST_F(ToolTipInfo, AutoTypeClassTemplateType)
{
const ::ToolTipInfo actual = tooltip(179, 5);
ASSERT_THAT(actual.text(), Utf8StringLiteral("Zii<int>"));
}
std::unique_ptr<Data> ToolTipInfo::d;
void ToolTipInfo::SetUpTestCase()
{
d.reset(new Data);
}
void ToolTipInfo::TearDownTestCase()
{
d.reset();
}
} // anonymous namespace

View File

@@ -33,3 +33,9 @@
#else #else
# define DISABLED_ON_WINDOWS(x) x # define DISABLED_ON_WINDOWS(x) x
#endif #endif
#ifdef IS_PRETTY_DECL_SUPPORTED
# define DISABLED_WITHOUT_PRETTYDECL_PATCH(x) x
#else
# define DISABLED_WITHOUT_PRETTYDECL_PATCH(x) DISABLED_##x
#endif

View File

@@ -0,0 +1,180 @@
void f(int foo, const int *cfoo)
{
foo++;
cfoo++;
}
struct Foo { int member = 0; };
int g(const Foo &foo)
{
return foo.member;
const Foo bar;
bar;
}
struct Bar { virtual ~Bar(); int mem(){} virtual int virtualConstMem() const; };
void h(const Foo &foo, Bar &bar)
{
g(foo);
bar.mem();
bar.virtualConstMem();
}
template <typename T>
void t(int foo) { (void)foo; }
void c()
{
t<Foo>(3);
}
/**
* \brief This is a crazy function.
*/
void documentedFunction();
void d()
{
documentedFunction();
}
enum EnumType { V1, V2, Custom = V2 + 5 };
EnumType e()
{
return EnumType::Custom;
}
template <typename T> struct Baz { T member; };
void t2(const Baz<int> &b) {
Baz<int> baz; baz = b;
}
#include "tooltipinfo.h"
#define MACRO_FROM_MAINFILE(x) x + 3
void foo()
{
MACRO_FROM_MAINFILE(7);
MACRO_FROM_HEADER(7);
}
namespace N { struct Muu{}; }
namespace G = N;
void o()
{
using namespace N;
Muu muu; (void)muu;
}
void n()
{
using namespace G;
Muu muu; (void)muu;
}
void q()
{
using N::Muu;
Muu muu; (void)muu;
}
struct Sizes
{
char memberChar1;
char memberChar2;
};
enum class FancyEnumType { V1, V2 };
union Union
{
char memberChar1;
char memberChar2;
};
namespace X {
namespace Y {
}
}
template<typename T> struct Ptr {};
struct Nuu {};
typedef Ptr<Nuu> PtrFromTypeDef;
using PtrFromTypeAlias = Ptr<Nuu>;
template<typename T> using PtrFromTemplateTypeAlias = Ptr<T>;
void y()
{
PtrFromTypeDef b; (void)b;
PtrFromTypeAlias a; (void)a;
PtrFromTemplateTypeAlias<Nuu> c; (void)c;
}
template <typename T> struct Zii {};
namespace U { template <typename T> struct Yii {}; }
void mc()
{
using namespace U;
Zii<int> zii; (void) zii;
Yii<int> yii; (void) yii;
}
namespace A { struct X {}; }
namespace B = A;
void ab()
{
B::X x; (void)x;
}
namespace N {
struct Outer
{
template <typename T> struct Inner {};
Inner<int> inner;
};
}
void f();
namespace R { void f(); }
void f(int param);
void z(int = 1);
void user()
{
f();
R::f();
f(1);
z();
}
void autoTypes()
{
auto a = 3; (void)a;
auto b = EnumType::V1; (void)b;
auto c = Bar(); (void)c;
auto d = Zii<int>(); (void)d;
}

View File

@@ -0,0 +1,3 @@
#define MACRO_FROM_HEADER(x) x + \
x + \
x

View File

@@ -38,4 +38,5 @@ public:
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &) override {} void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &) override {}
void references(const ClangBackEnd::ReferencesMessage &) override {} void references(const ClangBackEnd::ReferencesMessage &) override {}
void followSymbol(const ClangBackEnd::FollowSymbolMessage &) override {} void followSymbol(const ClangBackEnd::FollowSymbolMessage &) override {}
void tooltip(const ClangBackEnd::ToolTipMessage &) override {}
}; };

View File

@@ -41,6 +41,7 @@
#include <sourcelocationentry.h> #include <sourcelocationentry.h>
#include <sourcelocationscontainer.h> #include <sourcelocationscontainer.h>
#include <tokeninfos.h> #include <tokeninfos.h>
#include <tooltipinfo.h>
#include <cpptools/usages.h> #include <cpptools/usages.h>
@@ -269,6 +270,17 @@ std::ostream &operator<<(std::ostream &os, const ReferencesMessage &message)
return os; return os;
} }
std::ostream &operator<<(std::ostream &os, const ToolTipMessage &message)
{
os << "("
<< message.fileContainer() << ", "
<< message.ticketNumber() << ", "
<< message.toolTipInfo() << ", "
<< ")";
return os;
}
std::ostream &operator<<(std::ostream &os, const EchoMessage &/*message*/) std::ostream &operator<<(std::ostream &os, const EchoMessage &/*message*/)
{ {
return os << "()"; return os << "()";
@@ -385,7 +397,8 @@ std::ostream &operator<<(std::ostream &os, const FileContainer &container)
<< container.filePath() << ", " << container.filePath() << ", "
<< container.projectPartId() << ", " << container.projectPartId() << ", "
<< container.fileArguments() << ", " << container.fileArguments() << ", "
<< container.documentRevision(); << container.documentRevision() << ", "
<< container.textCodecName();
if (container.hasUnsavedFileContent()) if (container.hasUnsavedFileContent())
os << ", " os << ", "
@@ -552,6 +565,37 @@ std::ostream &operator<<(std::ostream &os, const RequestReferencesMessage &messa
return os; return os;
} }
std::ostream &operator<<(std::ostream &out, const RequestToolTipMessage &message)
{
out << "("
<< message.fileContainer() << ", "
<< message.ticketNumber() << ", "
<< message.line() << ", "
<< message.column() << ", "
<< ")";
return out;
}
std::ostream &operator<<(std::ostream &os, const ToolTipInfo::QdocCategory category)
{
return os << qdocCategoryToString(category);
}
std::ostream &operator<<(std::ostream &out, const ToolTipInfo &info)
{
out << "("
<< info.m_text << ", "
<< info.m_briefComment << ", "
<< info.m_qdocIdCandidates << ", "
<< info.m_qdocMark << ", "
<< info.m_qdocCategory
<< info.m_sizeInBytes << ", "
<< ")";
return out;
}
std::ostream &operator<<(std::ostream &os, const RequestSourceLocationsForRenamingMessage &message) std::ostream &operator<<(std::ostream &os, const RequestSourceLocationsForRenamingMessage &message)
{ {

View File

@@ -73,6 +73,7 @@ class CodeCompletedMessage;
class EchoMessage; class EchoMessage;
class DocumentAnnotationsChangedMessage; class DocumentAnnotationsChangedMessage;
class ReferencesMessage; class ReferencesMessage;
class ToolTipMessage;
class FollowSymbolMessage; class FollowSymbolMessage;
class CompleteCodeMessage; class CompleteCodeMessage;
class EndMessage; class EndMessage;
@@ -97,6 +98,7 @@ class RemovePchProjectPartsMessage;
class RequestDocumentAnnotationsMessage; class RequestDocumentAnnotationsMessage;
class RequestFollowSymbolMessage; class RequestFollowSymbolMessage;
class RequestReferencesMessage; class RequestReferencesMessage;
class RequestToolTipMessage;
class RequestSourceLocationsForRenamingMessage; class RequestSourceLocationsForRenamingMessage;
class RequestSourceRangesAndDiagnosticsForQueryMessage; class RequestSourceRangesAndDiagnosticsForQueryMessage;
class RequestSourceRangesForQueryMessage; class RequestSourceRangesForQueryMessage;
@@ -114,6 +116,7 @@ class UpdateVisibleTranslationUnitsMessage;
class FilePath; class FilePath;
class TokenInfo; class TokenInfo;
class TokenInfos; class TokenInfos;
class ToolTipInfo;
std::ostream &operator<<(std::ostream &out, const SourceLocationEntry &entry); std::ostream &operator<<(std::ostream &out, const SourceLocationEntry &entry);
std::ostream &operator<<(std::ostream &out, const IdPaths &idPaths); std::ostream &operator<<(std::ostream &out, const IdPaths &idPaths);
@@ -127,6 +130,7 @@ std::ostream &operator<<(std::ostream &out, const CodeCompletedMessage &message)
std::ostream &operator<<(std::ostream &out, const EchoMessage &message); std::ostream &operator<<(std::ostream &out, const EchoMessage &message);
std::ostream &operator<<(std::ostream &out, const DocumentAnnotationsChangedMessage &message); std::ostream &operator<<(std::ostream &out, const DocumentAnnotationsChangedMessage &message);
std::ostream &operator<<(std::ostream &out, const ReferencesMessage &message); std::ostream &operator<<(std::ostream &out, const ReferencesMessage &message);
std::ostream &operator<<(std::ostream &out, const ToolTipMessage &message);
std::ostream &operator<<(std::ostream &out, const FollowSymbolMessage &message); std::ostream &operator<<(std::ostream &out, const FollowSymbolMessage &message);
std::ostream &operator<<(std::ostream &out, const CompleteCodeMessage &message); std::ostream &operator<<(std::ostream &out, const CompleteCodeMessage &message);
std::ostream &operator<<(std::ostream &out, const EndMessage &message); std::ostream &operator<<(std::ostream &out, const EndMessage &message);
@@ -153,6 +157,8 @@ std::ostream &operator<<(std::ostream &out, const RemovePchProjectPartsMessage &
std::ostream &operator<<(std::ostream &out, const RequestDocumentAnnotationsMessage &message); std::ostream &operator<<(std::ostream &out, const RequestDocumentAnnotationsMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestFollowSymbolMessage &message); std::ostream &operator<<(std::ostream &out, const RequestFollowSymbolMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestReferencesMessage &message); std::ostream &operator<<(std::ostream &out, const RequestReferencesMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestToolTipMessage &message);
std::ostream &operator<<(std::ostream &out, const ToolTipInfo &info);
std::ostream &operator<<(std::ostream &out, const RequestSourceLocationsForRenamingMessage &message); std::ostream &operator<<(std::ostream &out, const RequestSourceLocationsForRenamingMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestSourceRangesAndDiagnosticsForQueryMessage &message); std::ostream &operator<<(std::ostream &out, const RequestSourceRangesAndDiagnosticsForQueryMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestSourceRangesForQueryMessage &message); std::ostream &operator<<(std::ostream &out, const RequestSourceRangesForQueryMessage &message);

View File

@@ -45,4 +45,6 @@ public:
void(const ClangBackEnd::ReferencesMessage &message)); void(const ClangBackEnd::ReferencesMessage &message));
MOCK_METHOD1(followSymbol, MOCK_METHOD1(followSymbol,
void(const ClangBackEnd::FollowSymbolMessage &message)); void(const ClangBackEnd::FollowSymbolMessage &message));
MOCK_METHOD1(tooltip,
void(const ClangBackEnd::ToolTipMessage &message));
}; };

View File

@@ -55,6 +55,8 @@ public:
void(const ClangBackEnd::RequestReferencesMessage &message)); void(const ClangBackEnd::RequestReferencesMessage &message));
MOCK_METHOD1(requestFollowSymbol, MOCK_METHOD1(requestFollowSymbol,
void(const ClangBackEnd::RequestFollowSymbolMessage &message)); void(const ClangBackEnd::RequestFollowSymbolMessage &message));
MOCK_METHOD1(requestToolTip,
void(const ClangBackEnd::RequestToolTipMessage &message));
MOCK_METHOD1(updateVisibleTranslationUnits, MOCK_METHOD1(updateVisibleTranslationUnits,
void(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message)); void(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message));
}; };

View File

@@ -118,6 +118,7 @@ SOURCES += \
clangstring-test.cpp \ clangstring-test.cpp \
clangsupportivetranslationunitinitializer-test.cpp \ clangsupportivetranslationunitinitializer-test.cpp \
clangsuspenddocumentjob-test.cpp \ clangsuspenddocumentjob-test.cpp \
clangtooltipinfo-test.cpp \
clangtranslationunits-test.cpp \ clangtranslationunits-test.cpp \
clangtranslationunit-test.cpp \ clangtranslationunit-test.cpp \
clangupdatedocumentannotationsjob-test.cpp \ clangupdatedocumentannotationsjob-test.cpp \

View File

@@ -203,4 +203,32 @@ TEST_F(UnsavedFile, HasCharacterForLastLineColumn)
ASSERT_TRUE(unsavedFile.hasCharacterAt(1, 7, 't')); ASSERT_TRUE(unsavedFile.hasCharacterAt(1, 7, 't'));
} }
TEST_F(UnsavedFile, LineRangeForInvalidLines)
{
::UnsavedFile unsavedFile(filePath, fileContent);
ASSERT_THAT(unsavedFile.lineRange(2, 1), Utf8String());
}
TEST_F(UnsavedFile, LineRangeForSingleLine)
{
::UnsavedFile unsavedFile(filePath, Utf8StringLiteral("foo"));
ASSERT_THAT(unsavedFile.lineRange(1, 1), Utf8StringLiteral("foo"));
}
TEST_F(UnsavedFile, LineRangeForSingleLineInMultipleLines)
{
::UnsavedFile unsavedFile(filePath, Utf8StringLiteral("foo\nbar\n\baz"));
ASSERT_THAT(unsavedFile.lineRange(2, 2), Utf8StringLiteral("bar"));
}
TEST_F(UnsavedFile, LineRangeForTwoLines)
{
::UnsavedFile unsavedFile(filePath, Utf8StringLiteral("foo\nbar\n\baz"));
ASSERT_THAT(unsavedFile.lineRange(2, 3), Utf8StringLiteral("bar\n\baz"));
}
} // anonymous namespace } // anonymous namespace