aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/libs/clangsupport/clangcodemodelclientinterface.cpp3
-rw-r--r--src/libs/clangsupport/clangcodemodelclientinterface.h3
-rw-r--r--src/libs/clangsupport/clangcodemodelclientmessages.h1
-rw-r--r--src/libs/clangsupport/clangcodemodelclientproxy.cpp5
-rw-r--r--src/libs/clangsupport/clangcodemodelclientproxy.h1
-rw-r--r--src/libs/clangsupport/clangcodemodelserverinterface.cpp3
-rw-r--r--src/libs/clangsupport/clangcodemodelserverinterface.h1
-rw-r--r--src/libs/clangsupport/clangcodemodelservermessages.h1
-rw-r--r--src/libs/clangsupport/clangcodemodelserverproxy.cpp5
-rw-r--r--src/libs/clangsupport/clangcodemodelserverproxy.h1
-rw-r--r--src/libs/clangsupport/clangsupport-lib.pri6
-rw-r--r--src/libs/clangsupport/clangsupport_global.h3
-rw-r--r--src/libs/clangsupport/filecontainer.cpp3
-rw-r--r--src/libs/clangsupport/filecontainer.h12
-rw-r--r--src/libs/clangsupport/messageenvelop.cpp6
-rw-r--r--src/libs/clangsupport/requesttooltipmessage.cpp48
-rw-r--r--src/libs/clangsupport/requesttooltipmessage.h94
-rw-r--r--src/libs/clangsupport/tooltipinfo.cpp65
-rw-r--r--src/libs/clangsupport/tooltipinfo.h124
-rw-r--r--src/libs/clangsupport/tooltipmessage.cpp44
-rw-r--r--src/libs/clangsupport/tooltipmessage.h87
-rw-r--r--src/libs/sqlite/utf8string.h3
-rw-r--r--src/plugins/clangcodemodel/clangbackendcommunicator.cpp9
-rw-r--r--src/plugins/clangcodemodel/clangbackendcommunicator.h3
-rw-r--r--src/plugins/clangcodemodel/clangbackendreceiver.cpp78
-rw-r--r--src/plugins/clangcodemodel/clangbackendreceiver.h5
-rw-r--r--src/plugins/clangcodemodel/clangbackendsender.cpp7
-rw-r--r--src/plugins/clangcodemodel/clangbackendsender.h1
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel.pro2
-rw-r--r--src/plugins/clangcodemodel/clangcodemodel.qbs2
-rw-r--r--src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp19
-rw-r--r--src/plugins/clangcodemodel/clangeditordocumentprocessor.h5
-rw-r--r--src/plugins/clangcodemodel/clanghoverhandler.cpp232
-rw-r--r--src/plugins/clangcodemodel/clanghoverhandler.h60
-rw-r--r--src/plugins/clangcodemodel/clangmodelmanagersupport.cpp4
-rw-r--r--src/plugins/cpptools/baseeditordocumentprocessor.cpp7
-rw-r--r--src/plugins/cpptools/baseeditordocumentprocessor.h14
-rw-r--r--src/plugins/cpptools/cpphoverhandler.cpp118
-rw-r--r--src/plugins/cpptools/cpphoverhandler.h7
-rw-r--r--src/plugins/texteditor/texteditor.cpp2
-rw-r--r--src/tools/clangbackend/source/clangbackend_global.h5
-rw-r--r--src/tools/clangbackend/source/clangbackendclangipc-source.pri4
-rw-r--r--src/tools/clangbackend/source/clangcodemodelserver.cpp20
-rw-r--r--src/tools/clangbackend/source/clangcodemodelserver.h1
-rw-r--r--src/tools/clangbackend/source/clangjobrequest.cpp15
-rw-r--r--src/tools/clangbackend/source/clangjobrequest.h2
-rw-r--r--src/tools/clangbackend/source/clangrequesttooltipjob.cpp67
-rw-r--r--src/tools/clangbackend/source/clangrequesttooltipjob.h43
-rw-r--r--src/tools/clangbackend/source/clangtooltipinfocollector.cpp531
-rw-r--r--src/tools/clangbackend/source/clangtooltipinfocollector.h45
-rw-r--r--src/tools/clangbackend/source/clangtranslationunit.cpp15
-rw-r--r--src/tools/clangbackend/source/clangtranslationunit.h5
-rw-r--r--src/tools/clangbackend/source/clangtype.cpp97
-rw-r--r--src/tools/clangbackend/source/clangtype.h4
-rw-r--r--src/tools/clangbackend/source/cursor.cpp38
-rw-r--r--src/tools/clangbackend/source/cursor.h11
-rw-r--r--src/tools/clangbackend/source/unsavedfile.cpp21
-rw-r--r--src/tools/clangbackend/source/unsavedfile.h1
-rw-r--r--tests/unit/echoserver/echoclangcodemodelserver.cpp5
-rw-r--r--tests/unit/echoserver/echoclangcodemodelserver.h1
-rw-r--r--tests/unit/unittest/clangtooltipinfo-test.cpp628
-rw-r--r--tests/unit/unittest/conditionally-disabled-tests.h6
-rw-r--r--tests/unit/unittest/data/tooltipinfo.cpp180
-rw-r--r--tests/unit/unittest/data/tooltipinfo.h3
-rw-r--r--tests/unit/unittest/dummyclangipcclient.h1
-rw-r--r--tests/unit/unittest/gtest-creator-printing.cpp46
-rw-r--r--tests/unit/unittest/gtest-creator-printing.h6
-rw-r--r--tests/unit/unittest/mockclangcodemodelclient.h2
-rw-r--r--tests/unit/unittest/mockclangcodemodelserver.h2
-rw-r--r--tests/unit/unittest/unittest.pro1
-rw-r--r--tests/unit/unittest/unsavedfile-test.cpp28
71 files changed, 2820 insertions, 108 deletions
diff --git a/src/libs/clangsupport/clangcodemodelclientinterface.cpp b/src/libs/clangsupport/clangcodemodelclientinterface.cpp
index 0de9f39044a..492ea7896fe 100644
--- a/src/libs/clangsupport/clangcodemodelclientinterface.cpp
+++ b/src/libs/clangsupport/clangcodemodelclientinterface.cpp
@@ -53,6 +53,9 @@ void ClangCodeModelClientInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::FollowSymbolMessage:
followSymbol(messageEnvelop.message<FollowSymbolMessage>());
break;
+ case MessageType::ToolTipMessage:
+ tooltip(messageEnvelop.message<ToolTipMessage>());
+ break;
default:
qWarning() << "Unknown ClangCodeModelClientMessage";
}
diff --git a/src/libs/clangsupport/clangcodemodelclientinterface.h b/src/libs/clangsupport/clangcodemodelclientinterface.h
index b6b878baaf0..6e8f3dd2485 100644
--- a/src/libs/clangsupport/clangcodemodelclientinterface.h
+++ b/src/libs/clangsupport/clangcodemodelclientinterface.h
@@ -43,6 +43,8 @@ class RegisterUnsavedFilesForEditorMessage;
class RequestDocumentAnnotationsMessage;
class RequestReferencesMessage;
class RequestFollowSymbolMessage;
+class RequestToolTipMessage;
+class ToolTipMessage;
class UnregisterProjectPartsForEditorMessage;
class UnregisterTranslationUnitsForEditorMessage;
class UnregisterUnsavedFilesForEditorMessage;
@@ -60,6 +62,7 @@ public:
virtual void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) = 0;
virtual void references(const ReferencesMessage &message) = 0;
virtual void followSymbol(const FollowSymbolMessage &message) = 0;
+ virtual void tooltip(const ToolTipMessage &message) = 0;
};
} // namespace ClangBackEnd
diff --git a/src/libs/clangsupport/clangcodemodelclientmessages.h b/src/libs/clangsupport/clangcodemodelclientmessages.h
index 5b5c9b42edb..2903cda2079 100644
--- a/src/libs/clangsupport/clangcodemodelclientmessages.h
+++ b/src/libs/clangsupport/clangcodemodelclientmessages.h
@@ -31,3 +31,4 @@
#include "documentannotationschangedmessage.h"
#include "referencesmessage.h"
#include "followsymbolmessage.h"
+#include "tooltipmessage.h"
diff --git a/src/libs/clangsupport/clangcodemodelclientproxy.cpp b/src/libs/clangsupport/clangcodemodelclientproxy.cpp
index 6a285c23668..fc75a226ed5 100644
--- a/src/libs/clangsupport/clangcodemodelclientproxy.cpp
+++ b/src/libs/clangsupport/clangcodemodelclientproxy.cpp
@@ -95,6 +95,11 @@ void ClangCodeModelClientProxy::followSymbol(const FollowSymbolMessage &message)
m_writeMessageBlock.write(message);
}
+void ClangCodeModelClientProxy::tooltip(const ToolTipMessage &message)
+{
+ m_writeMessageBlock.write(message);
+}
+
void ClangCodeModelClientProxy::readMessages()
{
for (const MessageEnvelop &message : m_readMessageBlock.readAll())
diff --git a/src/libs/clangsupport/clangcodemodelclientproxy.h b/src/libs/clangsupport/clangcodemodelclientproxy.h
index 0665d6e8faa..58b87a05d8b 100644
--- a/src/libs/clangsupport/clangcodemodelclientproxy.h
+++ b/src/libs/clangsupport/clangcodemodelclientproxy.h
@@ -57,6 +57,7 @@ public:
void documentAnnotationsChanged(const DocumentAnnotationsChangedMessage &message) override;
void references(const ReferencesMessage &message) override;
void followSymbol(const FollowSymbolMessage &message) override;
+ void tooltip(const ToolTipMessage &message) override;
void readMessages();
diff --git a/src/libs/clangsupport/clangcodemodelserverinterface.cpp b/src/libs/clangsupport/clangcodemodelserverinterface.cpp
index bdaa802a47e..d50e97367d5 100644
--- a/src/libs/clangsupport/clangcodemodelserverinterface.cpp
+++ b/src/libs/clangsupport/clangcodemodelserverinterface.cpp
@@ -71,6 +71,9 @@ void ClangCodeModelServerInterface::dispatch(const MessageEnvelop &messageEnvelo
case MessageType::RequestFollowSymbolMessage:
requestFollowSymbol(messageEnvelop.message<RequestFollowSymbolMessage>());
break;
+ case MessageType::RequestToolTipMessage:
+ requestToolTip(messageEnvelop.message<RequestToolTipMessage>());
+ break;
case MessageType::UpdateVisibleTranslationUnitsMessage:
updateVisibleTranslationUnits(messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>());
break;
diff --git a/src/libs/clangsupport/clangcodemodelserverinterface.h b/src/libs/clangsupport/clangcodemodelserverinterface.h
index e4e897afc4d..eb24fa4dfed 100644
--- a/src/libs/clangsupport/clangcodemodelserverinterface.h
+++ b/src/libs/clangsupport/clangcodemodelserverinterface.h
@@ -50,6 +50,7 @@ public:
virtual void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) = 0;
virtual void requestReferences(const RequestReferencesMessage &message) = 0;
virtual void requestFollowSymbol(const RequestFollowSymbolMessage &message) = 0;
+ virtual void requestToolTip(const RequestToolTipMessage &message) = 0;
virtual void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) = 0;
};
diff --git a/src/libs/clangsupport/clangcodemodelservermessages.h b/src/libs/clangsupport/clangcodemodelservermessages.h
index b1ba7a07016..0e577b08ddd 100644
--- a/src/libs/clangsupport/clangcodemodelservermessages.h
+++ b/src/libs/clangsupport/clangcodemodelservermessages.h
@@ -36,6 +36,7 @@
#include "requestdocumentannotations.h"
#include "requestreferencesmessage.h"
#include "requestfollowsymbolmessage.h"
+#include "requesttooltipmessage.h"
#include "unregisterunsavedfilesforeditormessage.h"
#include "updatetranslationunitsforeditormessage.h"
#include "updatevisibletranslationunitsmessage.h"
diff --git a/src/libs/clangsupport/clangcodemodelserverproxy.cpp b/src/libs/clangsupport/clangcodemodelserverproxy.cpp
index f51bcb2802b..23a80284802 100644
--- a/src/libs/clangsupport/clangcodemodelserverproxy.cpp
+++ b/src/libs/clangsupport/clangcodemodelserverproxy.cpp
@@ -96,6 +96,11 @@ void ClangCodeModelServerProxy::requestFollowSymbol(const RequestFollowSymbolMes
m_writeMessageBlock.write(message);
}
+void ClangCodeModelServerProxy::requestToolTip(const RequestToolTipMessage &message)
+{
+ m_writeMessageBlock.write(message);
+}
+
void ClangCodeModelServerProxy::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
m_writeMessageBlock.write(message);
diff --git a/src/libs/clangsupport/clangcodemodelserverproxy.h b/src/libs/clangsupport/clangcodemodelserverproxy.h
index e93e05d3f54..62ea90eebe2 100644
--- a/src/libs/clangsupport/clangcodemodelserverproxy.h
+++ b/src/libs/clangsupport/clangcodemodelserverproxy.h
@@ -60,6 +60,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
+ void requestToolTip(const RequestToolTipMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
};
diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri
index a32f78c1868..952fd72a10d 100644
--- a/src/libs/clangsupport/clangsupport-lib.pri
+++ b/src/libs/clangsupport/clangsupport-lib.pri
@@ -68,6 +68,7 @@ SOURCES += \
$$PWD/requestsourcelocationforrenamingmessage.cpp \
$$PWD/requestsourcerangesanddiagnosticsforquerymessage.cpp \
$$PWD/requestsourcerangesforquerymessage.cpp \
+ $$PWD/requesttooltipmessage.cpp \
$$PWD/sourcelocationcontainer.cpp \
$$PWD/sourcelocationcontainerv2.cpp \
$$PWD/sourcelocationscontainer.cpp \
@@ -82,6 +83,8 @@ SOURCES += \
$$PWD/sourcerangesforquerymessage.cpp \
$$PWD/sourcerangewithtextcontainer.cpp \
$$PWD/tokeninfocontainer.cpp \
+ $$PWD/tooltipmessage.cpp \
+ $$PWD/tooltipinfo.cpp \
$$PWD/unregisterunsavedfilesforeditormessage.cpp \
$$PWD/updatepchprojectpartsmessage.cpp \
$$PWD/updatetranslationunitsforeditormessage.cpp \
@@ -159,6 +162,7 @@ HEADERS += \
$$PWD/requestsourcelocationforrenamingmessage.h \
$$PWD/requestsourcerangesanddiagnosticsforquerymessage.h \
$$PWD/requestsourcerangesforquerymessage.h \
+ $$PWD/requesttooltipmessage.h \
$$PWD/sourcelocationcontainer.h \
$$PWD/sourcelocationcontainerv2.h \
$$PWD/sourcelocationscontainer.h \
@@ -176,6 +180,8 @@ HEADERS += \
$$PWD/sourcerangewithtextcontainer.h \
$$PWD/stringcache.h \
$$PWD/tokeninfocontainer.h \
+ $$PWD/tooltipmessage.h \
+ $$PWD/tooltipinfo.h \
$$PWD/unregisterunsavedfilesforeditormessage.h \
$$PWD/updatepchprojectpartsmessage.h \
$$PWD/updatetranslationunitsforeditormessage.h \
diff --git a/src/libs/clangsupport/clangsupport_global.h b/src/libs/clangsupport/clangsupport_global.h
index 15c2679e2e3..1a368162e1e 100644
--- a/src/libs/clangsupport/clangsupport_global.h
+++ b/src/libs/clangsupport/clangsupport_global.h
@@ -120,6 +120,9 @@ enum class MessageType : quint8 {
RequestFollowSymbolMessage,
FollowSymbolMessage,
+ RequestToolTipMessage,
+ ToolTipMessage,
+
UpdateVisibleTranslationUnitsMessage,
CompleteCodeMessage,
diff --git a/src/libs/clangsupport/filecontainer.cpp b/src/libs/clangsupport/filecontainer.cpp
index e2efc70b896..05f888aebf8 100644
--- a/src/libs/clangsupport/filecontainer.cpp
+++ b/src/libs/clangsupport/filecontainer.cpp
@@ -37,7 +37,8 @@ QDebug operator<<(QDebug debug, const FileContainer &container)
<< container.filePath() << ", "
<< container.projectPartId() << ", "
<< container.fileArguments() << ", "
- << container.documentRevision();
+ << container.documentRevision() << ", "
+ << container.textCodecName();
if (container.hasUnsavedFileContent()) {
const Utf8String fileWithContent = debugWriteFileForInspection(
diff --git a/src/libs/clangsupport/filecontainer.h b/src/libs/clangsupport/filecontainer.h
index c7cb3f10053..b19c0daa46a 100644
--- a/src/libs/clangsupport/filecontainer.h
+++ b/src/libs/clangsupport/filecontainer.h
@@ -42,10 +42,12 @@ public:
const Utf8String &projectPartId,
const Utf8String &unsavedFileContent = Utf8String(),
bool hasUnsavedFileContent = false,
- quint32 documentRevision = 0)
+ quint32 documentRevision = 0,
+ const Utf8String &textCodecName = Utf8String())
: m_filePath(filePath),
m_projectPartId(projectPartId),
m_unsavedFileContent(unsavedFileContent),
+ m_textCodecName(textCodecName),
m_documentRevision(documentRevision),
m_hasUnsavedFileContent(hasUnsavedFileContent)
{
@@ -98,6 +100,11 @@ public:
return m_unsavedFileContent;
}
+ const Utf8String &textCodecName() const
+ {
+ return m_textCodecName;
+ }
+
bool hasUnsavedFileContent() const
{
return m_hasUnsavedFileContent;
@@ -114,6 +121,7 @@ public:
out << container.m_projectPartId;
out << container.m_fileArguments;
out << container.m_unsavedFileContent;
+ out << container.m_textCodecName;
out << container.m_documentRevision;
out << container.m_hasUnsavedFileContent;
@@ -126,6 +134,7 @@ public:
in >> container.m_projectPartId;
in >> container.m_fileArguments;
in >> container.m_unsavedFileContent;
+ in >> container.m_textCodecName;
in >> container.m_documentRevision;
in >> container.m_hasUnsavedFileContent;
@@ -142,6 +151,7 @@ private:
Utf8String m_projectPartId;
Utf8StringVector m_fileArguments;
Utf8String m_unsavedFileContent;
+ Utf8String m_textCodecName;
quint32 m_documentRevision = 0;
bool m_hasUnsavedFileContent = false;
};
diff --git a/src/libs/clangsupport/messageenvelop.cpp b/src/libs/clangsupport/messageenvelop.cpp
index 58139c4c8fd..134616a5b93 100644
--- a/src/libs/clangsupport/messageenvelop.cpp
+++ b/src/libs/clangsupport/messageenvelop.cpp
@@ -68,6 +68,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::RequestReferencesMessage:
qDebug() << messageEnvelop.message<RequestReferencesMessage>();
break;
+ case MessageType::RequestToolTipMessage:
+ qDebug() << messageEnvelop.message<RequestToolTipMessage>();
+ break;
case MessageType::UpdateVisibleTranslationUnitsMessage:
qDebug() << messageEnvelop.message<UpdateVisibleTranslationUnitsMessage>();
break;
@@ -83,6 +86,9 @@ QDebug operator<<(QDebug debug, const MessageEnvelop &messageEnvelop)
case MessageType::ReferencesMessage:
qDebug() << messageEnvelop.message<ReferencesMessage>();
break;
+ case MessageType::ToolTipMessage:
+ qDebug() << messageEnvelop.message<ToolTipMessage>();
+ break;
case MessageType::DocumentAnnotationsChangedMessage:
qDebug() << messageEnvelop.message<DocumentAnnotationsChangedMessage>();
break;
diff --git a/src/libs/clangsupport/requesttooltipmessage.cpp b/src/libs/clangsupport/requesttooltipmessage.cpp
new file mode 100644
index 00000000000..77ffdb46d80
--- /dev/null
+++ b/src/libs/clangsupport/requesttooltipmessage.cpp
@@ -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
diff --git a/src/libs/clangsupport/requesttooltipmessage.h b/src/libs/clangsupport/requesttooltipmessage.h
new file mode 100644
index 00000000000..066d7358786
--- /dev/null
+++ b/src/libs/clangsupport/requesttooltipmessage.h
@@ -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
diff --git a/src/libs/clangsupport/tooltipinfo.cpp b/src/libs/clangsupport/tooltipinfo.cpp
new file mode 100644
index 00000000000..fc9a3394214
--- /dev/null
+++ b/src/libs/clangsupport/tooltipinfo.cpp
@@ -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
diff --git a/src/libs/clangsupport/tooltipinfo.h b/src/libs/clangsupport/tooltipinfo.h
new file mode 100644
index 00000000000..b210d0dfe66
--- /dev/null
+++ b/src/libs/clangsupport/tooltipinfo.h
@@ -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
diff --git a/src/libs/clangsupport/tooltipmessage.cpp b/src/libs/clangsupport/tooltipmessage.cpp
new file mode 100644
index 00000000000..6e64fe33694
--- /dev/null
+++ b/src/libs/clangsupport/tooltipmessage.cpp
@@ -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
diff --git a/src/libs/clangsupport/tooltipmessage.h b/src/libs/clangsupport/tooltipmessage.h
new file mode 100644
index 00000000000..5d748179a66
--- /dev/null
+++ b/src/libs/clangsupport/tooltipmessage.h
@@ -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
diff --git a/src/libs/sqlite/utf8string.h b/src/libs/sqlite/utf8string.h
index 3c0f48519a2..5aa38956d15 100644
--- a/src/libs/sqlite/utf8string.h
+++ b/src/libs/sqlite/utf8string.h
@@ -171,7 +171,8 @@ public:
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));
}
diff --git a/src/plugins/clangcodemodel/clangbackendcommunicator.cpp b/src/plugins/clangcodemodel/clangbackendcommunicator.cpp
index 56504793122..1abd6982edf 100644
--- a/src/plugins/clangcodemodel/clangbackendcommunicator.cpp
+++ b/src/plugins/clangcodemodel/clangbackendcommunicator.cpp
@@ -392,6 +392,15 @@ QFuture<CppTools::CursorInfo> BackendCommunicator::requestLocalReferences(
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(
const FileContainer &curFileContainer,
quint32 line,
diff --git a/src/plugins/clangcodemodel/clangbackendcommunicator.h b/src/plugins/clangcodemodel/clangbackendcommunicator.h
index bd7494a6c20..b3cb166a391 100644
--- a/src/plugins/clangcodemodel/clangbackendcommunicator.h
+++ b/src/plugins/clangcodemodel/clangbackendcommunicator.h
@@ -82,6 +82,9 @@ public:
quint32 line,
quint32 column,
QTextDocument *textDocument);
+ QFuture<CppTools::ToolTipInfo> requestToolTip(const FileContainer &fileContainer,
+ quint32 line,
+ quint32 column);
QFuture<CppTools::SymbolInfo> requestFollowSymbol(const FileContainer &curFileContainer,
quint32 line,
quint32 column);
diff --git a/src/plugins/clangcodemodel/clangbackendreceiver.cpp b/src/plugins/clangcodemodel/clangbackendreceiver.cpp
index 8d5ec92d00d..f2606d74cc5 100644
--- a/src/plugins/clangcodemodel/clangbackendreceiver.cpp
+++ b/src/plugins/clangcodemodel/clangbackendreceiver.cpp
@@ -128,6 +128,18 @@ QFuture<CppTools::SymbolInfo> BackendReceiver::addExpectedRequestFollowSymbolMes
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
{
return !m_assistProcessorsTable.isEmpty();
@@ -272,6 +284,72 @@ void BackendReceiver::references(const ReferencesMessage &message)
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)
{
qCDebugIpc() << "FollowSymbolMessage with"
diff --git a/src/plugins/clangcodemodel/clangbackendreceiver.h b/src/plugins/clangcodemodel/clangbackendreceiver.h
index d7a966eae98..921150a52dc 100644
--- a/src/plugins/clangcodemodel/clangbackendreceiver.h
+++ b/src/plugins/clangcodemodel/clangbackendreceiver.h
@@ -27,6 +27,7 @@
#include <cpptools/cppcursorinfo.h>
#include <cpptools/cppsymbolinfo.h>
+#include <cpptools/baseeditordocumentprocessor.h>
#include <clangsupport/clangcodemodelclientinterface.h>
@@ -59,6 +60,7 @@ public:
const CppTools::SemanticInfo::LocalUseMap &localUses
= CppTools::SemanticInfo::LocalUseMap());
QFuture<CppTools::SymbolInfo> addExpectedRequestFollowSymbolMessage(quint64 ticket);
+ QFuture<CppTools::ToolTipInfo> addExpectedToolTipMessage(quint64 ticket);
bool isExpectingCodeCompletedMessage() const;
void reset();
@@ -70,6 +72,7 @@ private:
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &message) override;
void references(const ClangBackEnd::ReferencesMessage &message) override;
+ void tooltip(const ClangBackEnd::ToolTipMessage &message) override;
void followSymbol(const ClangBackEnd::FollowSymbolMessage &message) override;
private:
@@ -89,7 +92,7 @@ private:
CppTools::SemanticInfo::LocalUseMap localUses;
};
QHash<quint64, ReferencesEntry> m_referencesTable;
-
+ QHash<quint64, QFutureInterface<CppTools::ToolTipInfo>> m_toolTipsTable;
QHash<quint64, QFutureInterface<CppTools::SymbolInfo>> m_followTable;
};
diff --git a/src/plugins/clangcodemodel/clangbackendsender.cpp b/src/plugins/clangcodemodel/clangbackendsender.cpp
index 089377083fb..f6f8aea1cf8 100644
--- a/src/plugins/clangcodemodel/clangbackendsender.cpp
+++ b/src/plugins/clangcodemodel/clangbackendsender.cpp
@@ -120,6 +120,13 @@ void BackendSender::requestReferences(const RequestReferencesMessage &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)
{
QTC_CHECK(m_connection->isConnected());
diff --git a/src/plugins/clangcodemodel/clangbackendsender.h b/src/plugins/clangcodemodel/clangbackendsender.h
index ca9238ae651..f01326a4dc3 100644
--- a/src/plugins/clangcodemodel/clangbackendsender.h
+++ b/src/plugins/clangcodemodel/clangbackendsender.h
@@ -48,6 +48,7 @@ public:
void completeCode(const ClangBackEnd::CompleteCodeMessage &message) override;
void requestDocumentAnnotations(const ClangBackEnd::RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const ClangBackEnd::RequestReferencesMessage &message) override;
+ void requestToolTip(const ClangBackEnd::RequestToolTipMessage &message) override;
void requestFollowSymbol(const ClangBackEnd::RequestFollowSymbolMessage &message) override;
void updateVisibleTranslationUnits(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message) override;
diff --git a/src/plugins/clangcodemodel/clangcodemodel.pro b/src/plugins/clangcodemodel/clangcodemodel.pro
index 9136db5db5c..0e444a350e2 100644
--- a/src/plugins/clangcodemodel/clangcodemodel.pro
+++ b/src/plugins/clangcodemodel/clangcodemodel.pro
@@ -30,6 +30,7 @@ SOURCES += \
clangfixitoperationsextractor.cpp \
clangfollowsymbol.cpp \
clangfunctionhintmodel.cpp \
+ clanghoverhandler.cpp \
clangtokeninfosreporter.cpp \
clangmodelmanagersupport.cpp \
clangpreprocessorassistproposalitem.cpp \
@@ -66,6 +67,7 @@ HEADERS += \
clangfixitoperationsextractor.h \
clangfollowsymbol.h \
clangfunctionhintmodel.h \
+ clanghoverhandler.h \
clangisdiagnosticrelatedtolocation.h \
clangmodelmanagersupport.h \
clangpreprocessorassistproposalitem.h \
diff --git a/src/plugins/clangcodemodel/clangcodemodel.qbs b/src/plugins/clangcodemodel/clangcodemodel.qbs
index cc81025925f..90105b86d38 100644
--- a/src/plugins/clangcodemodel/clangcodemodel.qbs
+++ b/src/plugins/clangcodemodel/clangcodemodel.qbs
@@ -80,6 +80,8 @@ QtcPlugin {
"clangfollowsymbol.h",
"clangfunctionhintmodel.cpp",
"clangfunctionhintmodel.h",
+ "clanghoverhandler.cpp",
+ "clanghoverhandler.h",
"clangtokeninfosreporter.cpp",
"clangtokeninfosreporter.h",
"clangisdiagnosticrelatedtolocation.h",
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
index 6eb98b2c92f..a282350f246 100644
--- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
+++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.cpp
@@ -375,6 +375,15 @@ ClangEditorDocumentProcessor::requestFollowSymbol(int line, int 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
{
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;
if (m_projectPart)
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)
diff --git a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
index 2bb7ca04633..67745b582cb 100644
--- a/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
+++ b/src/plugins/clangcodemodel/clangeditordocumentprocessor.h
@@ -88,6 +88,9 @@ public:
QFuture<CppTools::CursorInfo> cursorInfo(const CppTools::CursorInfoParams &params) override;
QFuture<CppTools::CursorInfo> requestLocalReferences(const QTextCursor &cursor) 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;
@@ -106,7 +109,7 @@ private:
void requestDocumentAnnotations(const QString &projectpartId);
HeaderErrorDiagnosticWidgetCreator creatorForHeaderErrorDiagnosticWidget(
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 fileContainerWithArgumentsAndDocumentContent(
CppTools::ProjectPart *projectPart) const;
diff --git a/src/plugins/clangcodemodel/clanghoverhandler.cpp b/src/plugins/clangcodemodel/clanghoverhandler.cpp
new file mode 100644
index 00000000000..6610b555c22
--- /dev/null
+++ b/src/plugins/clangcodemodel/clanghoverhandler.cpp
@@ -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
diff --git a/src/plugins/clangcodemodel/clanghoverhandler.h b/src/plugins/clangcodemodel/clanghoverhandler.h
new file mode 100644
index 00000000000..85b7999f94b
--- /dev/null
+++ b/src/plugins/clangcodemodel/clanghoverhandler.h
@@ -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
diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
index 9271975dfde..9495e6d5ebc 100644
--- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
+++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp
@@ -29,11 +29,11 @@
#include "clangeditordocumentprocessor.h"
#include "clangutils.h"
#include "clangfollowsymbol.h"
+#include "clanghoverhandler.h"
#include "clangrefactoringengine.h"
#include <coreplugin/editormanager/editormanager.h>
#include <cpptools/cppfollowsymbolundercursor.h>
-#include <cpptools/cpphoverhandler.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/editordocumenthandle.h>
#include <cpptools/projectinfo.h>
@@ -114,7 +114,7 @@ CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssis
TextEditor::BaseHoverHandler *ModelManagerSupportClang::createHoverHandler()
{
- return new CppTools::CppHoverHandler;
+ return new Internal::ClangHoverHandler;
}
CppTools::FollowSymbolInterface &ModelManagerSupportClang::followSymbolInterface()
diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.cpp b/src/plugins/cpptools/baseeditordocumentprocessor.cpp
index 5bffb51a382..20632289c68 100644
--- a/src/plugins/cpptools/baseeditordocumentprocessor.cpp
+++ b/src/plugins/cpptools/baseeditordocumentprocessor.cpp
@@ -97,6 +97,13 @@ void BaseEditorDocumentProcessor::setParserConfig(
parser()->setConfiguration(config);
}
+QFuture<ToolTipInfo> BaseEditorDocumentProcessor::toolTipInfo(const QByteArray &/*codecName*/,
+ int /*line*/,
+ int /*column*/)
+{
+ return QFuture<ToolTipInfo>();
+}
+
void BaseEditorDocumentProcessor::runParser(QFutureInterface<void> &future,
BaseEditorDocumentParser::Ptr parser,
BaseEditorDocumentParser::UpdateParams updateParams)
diff --git a/src/plugins/cpptools/baseeditordocumentprocessor.h b/src/plugins/cpptools/baseeditordocumentprocessor.h
index b3f96d466de..d799c636b25 100644
--- a/src/plugins/cpptools/baseeditordocumentprocessor.h
+++ b/src/plugins/cpptools/baseeditordocumentprocessor.h
@@ -32,6 +32,7 @@
#include "cpptools_global.h"
#include <texteditor/codeassist/assistinterface.h>
+#include <texteditor/helpitem.h>
#include <texteditor/quickfix.h>
#include <texteditor/texteditor.h>
#include <texteditor/textdocument.h>
@@ -48,6 +49,18 @@ class TextDocument;
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
{
Q_OBJECT
@@ -78,6 +91,7 @@ public:
virtual QFuture<CursorInfo> cursorInfo(const CursorInfoParams &params) = 0;
virtual QFuture<CursorInfo> requestLocalReferences(const QTextCursor &cursor) = 0;
virtual QFuture<SymbolInfo> requestFollowSymbol(int line, int column) = 0;
+ virtual QFuture<ToolTipInfo> toolTipInfo(const QByteArray &codecName, int line, int column);
public:
using HeaderErrorDiagnosticWidgetCreator = std::function<QWidget*()>;
diff --git a/src/plugins/cpptools/cpphoverhandler.cpp b/src/plugins/cpptools/cpphoverhandler.cpp
index 9b9a8edff74..1ff41b7fdc3 100644
--- a/src/plugins/cpptools/cpphoverhandler.cpp
+++ b/src/plugins/cpptools/cpphoverhandler.cpp
@@ -28,78 +28,47 @@
#include "cppelementevaluator.h"
#include <coreplugin/helpmanager.h>
-#include <cpptools/baseeditordocumentprocessor.h>
-#include <cpptools/cppmodelmanager.h>
-#include <cpptools/editordocumenthandle.h>
#include <texteditor/texteditor.h>
#include <utils/textutils.h>
-#include <utils/qtcassert.h>
-#include <utils/tooltip/tooltip.h>
#include <QTextCursor>
#include <QUrl>
-#include <QVBoxLayout>
using namespace Core;
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;
-}
+namespace CppTools {
-bool editorDocumentProcessorHasDiagnosticAt(TextEditorWidget *editorWidget, int pos)
+QString CppHoverHandler::tooltipTextForHelpItem(const HelpItem &helpItem)
{
- if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) {
- int line, column;
- if (Utils::Text::convertPosition(editorWidget->document(), pos, &line, &column))
- return processor->hasDiagnosticsAt(line, column);
+ // 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 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);
- }
- }
+ return QString();
}
-} // anonymous namespace
-
-namespace CppTools {
-
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());
tc.setPosition(pos);
@@ -135,9 +104,6 @@ void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos)
void CppHoverHandler::decorateToolTip()
{
- if (m_positionForEditorDocumentProcessor != -1)
- return;
-
if (Qt::mightBeRichText(toolTip()))
setToolTip(toolTip().toHtmlEscaped());
@@ -146,42 +112,10 @@ void CppHoverHandler::decorateToolTip()
const HelpItem &help = lastHelpItemIdentified();
if (help.isValid()) {
- // 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 = help.category();
- 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());
- }
+ const QString text = tooltipTextForHelpItem(help);
+ if (!text.isEmpty())
+ setToolTip(text);
}
}
-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
diff --git a/src/plugins/cpptools/cpphoverhandler.h b/src/plugins/cpptools/cpphoverhandler.h
index b728b970fe4..edfa6901cf8 100644
--- a/src/plugins/cpptools/cpphoverhandler.h
+++ b/src/plugins/cpptools/cpphoverhandler.h
@@ -33,13 +33,12 @@ namespace CppTools {
class CPPTOOLS_EXPORT CppHoverHandler : public TextEditor::BaseHoverHandler
{
+public:
+ static QString tooltipTextForHelpItem(const TextEditor::HelpItem &help);
+
private:
void identifyMatch(TextEditor::TextEditorWidget *editorWidget, int pos) override;
void decorateToolTip() override;
- void operateTooltip(TextEditor::TextEditorWidget *editorWidget, const QPoint &point) override;
-
-private:
- int m_positionForEditorDocumentProcessor = -1;
};
} // namespace CppTools
diff --git a/src/plugins/texteditor/texteditor.cpp b/src/plugins/texteditor/texteditor.cpp
index c3fb788d25b..4b2b428f019 100644
--- a/src/plugins/texteditor/texteditor.cpp
+++ b/src/plugins/texteditor/texteditor.cpp
@@ -309,6 +309,7 @@ public:
const int documentRevision = textCursor.document()->revision();
const int position = Text::wordStartCursor(textCursor).position();
if (m_lastHandlerInfo.applies(documentRevision, position)) {
+ qDebug() << "Last handler applies, showing it";
m_lastHandlerInfo.handler->showToolTip(m_widget, point, /*decorate=*/ false);
return;
}
@@ -364,6 +365,7 @@ public:
// All were queried, run the best
if (m_bestHandler) {
+ qDebug() << "setting last handler info:" << m_documentRevision << m_position;
m_lastHandlerInfo = LastHandlerInfo(m_bestHandler, m_documentRevision, m_position);
m_bestHandler->showToolTip(m_widget, m_point);
}
diff --git a/src/tools/clangbackend/source/clangbackend_global.h b/src/tools/clangbackend/source/clangbackend_global.h
index e62c9a35130..a7ca7859587 100644
--- a/src/tools/clangbackend/source/clangbackend_global.h
+++ b/src/tools/clangbackend/source/clangbackend_global.h
@@ -36,4 +36,9 @@ enum class PreferredTranslationUnit
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
diff --git a/src/tools/clangbackend/source/clangbackendclangipc-source.pri b/src/tools/clangbackend/source/clangbackendclangipc-source.pri
index 3460e9d297d..154b5cc0194 100644
--- a/src/tools/clangbackend/source/clangbackendclangipc-source.pri
+++ b/src/tools/clangbackend/source/clangbackendclangipc-source.pri
@@ -29,10 +29,12 @@ HEADERS += \
$$PWD/clangreparsesupportivetranslationunitjob.h \
$$PWD/clangrequestdocumentannotationsjob.h \
$$PWD/clangrequestreferencesjob.h \
+ $$PWD/clangrequesttooltipjob.h \
$$PWD/clangresumedocumentjob.h \
$$PWD/clangstring.h \
$$PWD/clangsupportivetranslationunitinitializer.h \
$$PWD/clangsuspenddocumentjob.h \
+ $$PWD/clangtooltipinfocollector.h \
$$PWD/clangtranslationunit.h \
$$PWD/clangtranslationunits.h \
$$PWD/clangtranslationunitupdater.h \
@@ -86,8 +88,10 @@ SOURCES += \
$$PWD/clangreparsesupportivetranslationunitjob.cpp \
$$PWD/clangrequestdocumentannotationsjob.cpp \
$$PWD/clangrequestreferencesjob.cpp \
+ $$PWD/clangrequesttooltipjob.cpp \
$$PWD/clangsuspenddocumentjob.cpp \
$$PWD/clangsupportivetranslationunitinitializer.cpp \
+ $$PWD/clangtooltipinfocollector.cpp \
$$PWD/clangtranslationunit.cpp \
$$PWD/clangtranslationunits.cpp \
$$PWD/clangtranslationunitupdater.cpp \
diff --git a/src/tools/clangbackend/source/clangcodemodelserver.cpp b/src/tools/clangbackend/source/clangcodemodelserver.cpp
index 6e3a4290b80..609ac1da85c 100644
--- a/src/tools/clangbackend/source/clangcodemodelserver.cpp
+++ b/src/tools/clangbackend/source/clangcodemodelserver.cpp
@@ -258,6 +258,7 @@ static void fillJobRequest(JobRequest &jobRequest, const MessageType &message)
jobRequest.line = message.line();
jobRequest.column = message.column();
jobRequest.ticketNumber = message.ticketNumber();
+ jobRequest.textCodecName = message.fileContainer().textCodecName();
// The unsaved files might get updater later, so take the current
// revision for the request.
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)
{
qCDebug(serverLog) << "########## updateVisibleTranslationUnits";
diff --git a/src/tools/clangbackend/source/clangcodemodelserver.h b/src/tools/clangbackend/source/clangcodemodelserver.h
index 1591d25743c..98508f348c5 100644
--- a/src/tools/clangbackend/source/clangcodemodelserver.h
+++ b/src/tools/clangbackend/source/clangcodemodelserver.h
@@ -61,6 +61,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
+ void requestToolTip(const RequestToolTipMessage &message) override;
public: // for tests
const Documents &documentsForTestOnly() const;
diff --git a/src/tools/clangbackend/source/clangjobrequest.cpp b/src/tools/clangbackend/source/clangjobrequest.cpp
index b40ea424257..e2c6f8f6d49 100644
--- a/src/tools/clangbackend/source/clangjobrequest.cpp
+++ b/src/tools/clangbackend/source/clangjobrequest.cpp
@@ -32,6 +32,7 @@
#include "clangreparsesupportivetranslationunitjob.h"
#include "clangrequestdocumentannotationsjob.h"
#include "clangrequestreferencesjob.h"
+#include "clangrequesttooltipjob.h"
#include "clangresumedocumentjob.h"
#include "clangsuspenddocumentjob.h"
#include "clangupdatedocumentannotationsjob.h"
@@ -40,6 +41,7 @@
#include <clangsupport/cmbcodecompletedmessage.h>
#include <clangsupport/followsymbolmessage.h>
#include <clangsupport/referencesmessage.h>
+#include <clangsupport/tooltipmessage.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(RequestReferences);
RETURN_TEXT_FOR_CASE(FollowSymbol);
+ RETURN_TEXT_FOR_CASE(RequestToolTip);
RETURN_TEXT_FOR_CASE(SuspendDocument);
RETURN_TEXT_FOR_CASE(ResumeDocument);
}
@@ -126,6 +129,7 @@ static JobRequest::ExpirationConditions expirationConditionsForType(JobRequest::
return Conditions(Condition::AnythingChanged);
case Type::RequestReferences:
case Type::RequestDocumentAnnotations:
+ case Type::RequestToolTip:
case Type::FollowSymbol:
return Conditions(Condition::DocumentClosed)
| Conditions(Condition::DocumentRevisionChanged);
@@ -153,8 +157,10 @@ static JobRequest::RunConditions conditionsForType(JobRequest::Type type)
Conditions conditions = Conditions(Condition::DocumentUnsuspended)
| Conditions(Condition::DocumentVisible);
- if (type == Type::RequestReferences || type == Type::FollowSymbol)
+ if (type == Type::RequestReferences || type == Type::FollowSymbol
+ || type == Type::RequestToolTip) {
conditions |= Condition::CurrentDocumentRevision;
+ }
if (type != Type::UpdateDocumentAnnotations && type != Type::ParseSupportiveTranslationUnit)
conditions |= Condition::DocumentParsed;
@@ -192,6 +198,8 @@ IAsyncJob *JobRequest::createJob() const
return new RequestDocumentAnnotationsJob();
case JobRequest::Type::RequestReferences:
return new RequestReferencesJob();
+ case JobRequest::Type::RequestToolTip:
+ return new RequestToolTipJob();
case JobRequest::Type::FollowSymbol:
return new FollowSymbolJob();
case JobRequest::Type::SuspendDocument:
@@ -224,6 +232,11 @@ void JobRequest::cancelJob(ClangCodeModelClientInterface &client) const
false,
ticketNumber));
break;
+ case JobRequest::Type::RequestToolTip:
+ client.tooltip(ToolTipMessage(FileContainer(),
+ ToolTipInfo(),
+ ticketNumber));
+ break;
case JobRequest::Type::CompleteCode:
client.codeCompleted(CodeCompletedMessage(CodeCompletions(),
CompletionCorrection::NoCorrection,
diff --git a/src/tools/clangbackend/source/clangjobrequest.h b/src/tools/clangbackend/source/clangjobrequest.h
index 4b79f22792e..81c78ba9dbc 100644
--- a/src/tools/clangbackend/source/clangjobrequest.h
+++ b/src/tools/clangbackend/source/clangjobrequest.h
@@ -59,6 +59,7 @@ public:
RequestDocumentAnnotations,
RequestReferences,
FollowSymbol,
+ RequestToolTip,
SuspendDocument,
ResumeDocument,
@@ -118,6 +119,7 @@ public:
qint32 funcNameStartLine = -1;
qint32 funcNameStartColumn = -1;
quint64 ticketNumber = 0;
+ Utf8String textCodecName;
bool localReferences = false;
};
diff --git a/src/tools/clangbackend/source/clangrequesttooltipjob.cpp b/src/tools/clangbackend/source/clangrequesttooltipjob.cpp
new file mode 100644
index 00000000000..0e1249e4e8b
--- /dev/null
+++ b/src/tools/clangbackend/source/clangrequesttooltipjob.cpp
@@ -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
diff --git a/src/tools/clangbackend/source/clangrequesttooltipjob.h b/src/tools/clangbackend/source/clangrequesttooltipjob.h
new file mode 100644
index 00000000000..eb121402cb2
--- /dev/null
+++ b/src/tools/clangbackend/source/clangrequesttooltipjob.h
@@ -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
diff --git a/src/tools/clangbackend/source/clangtooltipinfocollector.cpp b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp
new file mode 100644
index 00000000000..44dde1edbbc
--- /dev/null
+++ b/src/tools/clangbackend/source/clangtooltipinfocollector.cpp
@@ -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
diff --git a/src/tools/clangbackend/source/clangtooltipinfocollector.h b/src/tools/clangbackend/source/clangtooltipinfocollector.h
new file mode 100644
index 00000000000..8009aa8bf4a
--- /dev/null
+++ b/src/tools/clangbackend/source/clangtooltipinfocollector.h
@@ -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
diff --git a/src/tools/clangbackend/source/clangtranslationunit.cpp b/src/tools/clangbackend/source/clangtranslationunit.cpp
index 94827801136..da06999b614 100644
--- a/src/tools/clangbackend/source/clangtranslationunit.cpp
+++ b/src/tools/clangbackend/source/clangtranslationunit.cpp
@@ -27,6 +27,7 @@
#include "clangbackend_global.h"
#include "clangreferencescollector.h"
+#include "clangtooltipinfocollector.h"
#include "clangtranslationunitupdater.h"
#include "clangfollowsymbol.h"
#include "clangfollowsymboljob.h"
@@ -139,6 +140,20 @@ void TranslationUnit::extractDocumentAnnotations(
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
{
return collectReferences(m_cxTranslationUnit, line, column, localReferences);
diff --git a/src/tools/clangbackend/source/clangtranslationunit.h b/src/tools/clangbackend/source/clangtranslationunit.h
index 76ba3f00551..150645cc362 100644
--- a/src/tools/clangbackend/source/clangtranslationunit.h
+++ b/src/tools/clangbackend/source/clangtranslationunit.h
@@ -41,6 +41,7 @@ class SkippedSourceRanges;
class SourceLocation;
class SourceRange;
class SourceRangeContainer;
+class ToolTipInfo;
class TranslationUnitUpdateInput;
class TranslationUnitUpdateResult;
class UnsavedFiles;
@@ -86,6 +87,10 @@ public:
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;
SourceLocation sourceLocationAt(uint line, uint column) const;
diff --git a/src/tools/clangbackend/source/clangtype.cpp b/src/tools/clangbackend/source/clangtype.cpp
index 8fc09d8c4af..970b9452c1d 100644
--- a/src/tools/clangbackend/source/clangtype.cpp
+++ b/src/tools/clangbackend/source/clangtype.cpp
@@ -84,6 +84,16 @@ bool Type::isBuiltinType() const
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
{
return ClangString(clang_getTypeSpelling(cxType));
@@ -94,6 +104,83 @@ ClangString Type::spelling() const
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
{
return clang_getNumArgTypes(cxType);
@@ -129,6 +216,16 @@ Cursor Type::declaration() const
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
{
return cxType.kind;
diff --git a/src/tools/clangbackend/source/clangtype.h b/src/tools/clangbackend/source/clangtype.h
index f26fe080f6b..6be968d3901 100644
--- a/src/tools/clangbackend/source/clangtype.h
+++ b/src/tools/clangbackend/source/clangtype.h
@@ -53,9 +53,11 @@ public:
bool isReferencingConstant() const;
bool isOutputArgument() const;
bool isBuiltinType() const;
+ bool isUnsigned() const;
Utf8String utf8Spelling() const;
ClangString spelling() const;
+ Utf8String builtinTypeToString() const;
int argumentCount() const;
Type alias() const;
@@ -66,6 +68,8 @@ public:
Cursor declaration() const;
+ long long sizeOf(bool *isValid) const;
+
CXTypeKind kind() const;
private:
diff --git a/src/tools/clangbackend/source/cursor.cpp b/src/tools/clangbackend/source/cursor.cpp
index 6ad50307cdd..41a0465e7a0 100644
--- a/src/tools/clangbackend/source/cursor.cpp
+++ b/src/tools/clangbackend/source/cursor.cpp
@@ -158,6 +158,14 @@ bool Cursor::isTemplateLike() const
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 hasFinal = false;
@@ -248,11 +256,31 @@ Type Cursor::nonPointerTupe() const
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
{
return clang_getSpecializedCursorTemplate(cxCursor);
}
+CXFile Cursor::includedFile() const
+{
+ return clang_getIncludedFile(cxCursor);
+}
+
SourceLocation Cursor::sourceLocation() const
{
return clang_getCursorLocation(cxCursor);
@@ -341,6 +369,11 @@ Cursor Cursor::functionBase() const
return functionBaseCursor;
}
+Type Cursor::resultType() const
+{
+ return clang_getResultType(type().cxType);
+}
+
Cursor Cursor::argument(int index) const
{
return clang_Cursor_getArgument(cxCursor, index);
@@ -398,6 +431,11 @@ CXCursorKind Cursor::kind() const
return clang_getCursorKind(cxCursor);
}
+CXCursor Cursor::cx() const
+{
+ return cxCursor;
+}
+
bool operator==(const Cursor &first, const Cursor &second)
{
return clang_equalCursors(first.cxCursor, second.cxCursor);
diff --git a/src/tools/clangbackend/source/cursor.h b/src/tools/clangbackend/source/cursor.h
index 6520366b36d..bef5a006f4f 100644
--- a/src/tools/clangbackend/source/cursor.h
+++ b/src/tools/clangbackend/source/cursor.h
@@ -67,6 +67,7 @@ public:
bool isFunctionLike() const;
bool isConstructorOrDestructor() const;
bool isTemplateLike() const;
+ bool isAnyTypeAlias() const;
bool hasFinalFunctionAttribute() const;
bool hasFinalClassAttribute() const;
bool isUnexposed() const;
@@ -81,6 +82,10 @@ public:
Type type() const;
Type nonPointerTupe() const;
+ Type enumType() const;
+
+ long long enumConstantValue() const;
+ unsigned long long enumConstantUnsignedValue() const;
SourceLocation sourceLocation() const;
CXSourceLocation cxSourceLocation() const;
@@ -92,17 +97,19 @@ public:
Cursor definition() const;
Cursor canonical() const;
- Cursor alias() const;
Cursor referenced() const;
Cursor semanticParent() const;
Cursor lexicalParent() const;
Cursor functionBaseDeclaration() const;
Cursor functionBase() const;
+ Type resultType() const;
Cursor argument(int index) const;
unsigned overloadedDeclarationsCount() const;
Cursor overloadedDeclaration(unsigned index) const;
Cursor specializedCursorTemplate() const;
+ CXFile includedFile() const;
+
void collectOutputArgumentRangesTo(
std::vector<CXSourceRange> &outputArgumentRanges) const;
std::vector<CXSourceRange> outputArgumentRanges() const;
@@ -112,6 +119,8 @@ public:
template <class VisitorCallback>
void visit(VisitorCallback visitorCallback) const;
+ CXCursor cx() const;
+
private:
CXCursor cxCursor;
};
diff --git a/src/tools/clangbackend/source/unsavedfile.cpp b/src/tools/clangbackend/source/unsavedfile.cpp
index 923d4456e8a..efcad4cad47 100644
--- a/src/tools/clangbackend/source/unsavedfile.cpp
+++ b/src/tools/clangbackend/source/unsavedfile.cpp
@@ -30,6 +30,8 @@
#include <ostream>
+#include <utils/qtcassert.h>
+
namespace ClangBackEnd {
UnsavedFile::UnsavedFile()
@@ -79,6 +81,25 @@ bool UnsavedFile::hasCharacterAt(uint line, uint column, char character) const
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
{
if (position < uint(m_fileContent.byteSize()))
diff --git a/src/tools/clangbackend/source/unsavedfile.h b/src/tools/clangbackend/source/unsavedfile.h
index 3021da89b5c..325139d240d 100644
--- a/src/tools/clangbackend/source/unsavedfile.h
+++ b/src/tools/clangbackend/source/unsavedfile.h
@@ -46,6 +46,7 @@ public:
// 1-based line and column
uint toUtf8Position(uint line, uint column, bool *ok) const;
bool hasCharacterAt(uint line, uint column, char character) const;
+ Utf8String lineRange(uint fromLine, uint toLine) const;
// 0-based position
bool hasCharacterAt(uint position, char character) const;
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.cpp b/tests/unit/echoserver/echoclangcodemodelserver.cpp
index 2c0fea98c93..38d2d395444 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.cpp
+++ b/tests/unit/echoserver/echoclangcodemodelserver.cpp
@@ -98,6 +98,11 @@ void EchoClangCodeModelServer::requestFollowSymbol(const RequestFollowSymbolMess
echoMessage(message);
}
+void EchoClangCodeModelServer::requestToolTip(const RequestToolTipMessage &message)
+{
+ echoMessage(message);
+}
+
void EchoClangCodeModelServer::updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message)
{
echoMessage(message);
diff --git a/tests/unit/echoserver/echoclangcodemodelserver.h b/tests/unit/echoserver/echoclangcodemodelserver.h
index 2dc8c30913a..d3452d2d321 100644
--- a/tests/unit/echoserver/echoclangcodemodelserver.h
+++ b/tests/unit/echoserver/echoclangcodemodelserver.h
@@ -47,6 +47,7 @@ public:
void requestDocumentAnnotations(const RequestDocumentAnnotationsMessage &message) override;
void requestReferences(const RequestReferencesMessage &message) override;
void requestFollowSymbol(const RequestFollowSymbolMessage &message) override;
+ void requestToolTip(const RequestToolTipMessage &message) override;
void updateVisibleTranslationUnits(const UpdateVisibleTranslationUnitsMessage &message) override;
private:
diff --git a/tests/unit/unittest/clangtooltipinfo-test.cpp b/tests/unit/unittest/clangtooltipinfo-test.cpp
new file mode 100644
index 00000000000..4f6c9806496
--- /dev/null
+++ b/tests/unit/unittest/clangtooltipinfo-test.cpp
@@ -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
diff --git a/tests/unit/unittest/conditionally-disabled-tests.h b/tests/unit/unittest/conditionally-disabled-tests.h
index 27e6fe173b2..8f55db10d7a 100644
--- a/tests/unit/unittest/conditionally-disabled-tests.h
+++ b/tests/unit/unittest/conditionally-disabled-tests.h
@@ -33,3 +33,9 @@
#else
# define DISABLED_ON_WINDOWS(x) x
#endif
+
+#ifdef IS_PRETTY_DECL_SUPPORTED
+# define DISABLED_WITHOUT_PRETTYDECL_PATCH(x) x
+#else
+# define DISABLED_WITHOUT_PRETTYDECL_PATCH(x) DISABLED_##x
+#endif
diff --git a/tests/unit/unittest/data/tooltipinfo.cpp b/tests/unit/unittest/data/tooltipinfo.cpp
new file mode 100644
index 00000000000..1945577b635
--- /dev/null
+++ b/tests/unit/unittest/data/tooltipinfo.cpp
@@ -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;
+}
diff --git a/tests/unit/unittest/data/tooltipinfo.h b/tests/unit/unittest/data/tooltipinfo.h
new file mode 100644
index 00000000000..47d3bb92e75
--- /dev/null
+++ b/tests/unit/unittest/data/tooltipinfo.h
@@ -0,0 +1,3 @@
+#define MACRO_FROM_HEADER(x) x + \
+ x + \
+ x
diff --git a/tests/unit/unittest/dummyclangipcclient.h b/tests/unit/unittest/dummyclangipcclient.h
index 3908f9e89f2..e840b0a6c7a 100644
--- a/tests/unit/unittest/dummyclangipcclient.h
+++ b/tests/unit/unittest/dummyclangipcclient.h
@@ -38,4 +38,5 @@ public:
void documentAnnotationsChanged(const ClangBackEnd::DocumentAnnotationsChangedMessage &) override {}
void references(const ClangBackEnd::ReferencesMessage &) override {}
void followSymbol(const ClangBackEnd::FollowSymbolMessage &) override {}
+ void tooltip(const ClangBackEnd::ToolTipMessage &) override {}
};
diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp
index 2c106ec0e53..1bcf5f2b07d 100644
--- a/tests/unit/unittest/gtest-creator-printing.cpp
+++ b/tests/unit/unittest/gtest-creator-printing.cpp
@@ -41,6 +41,7 @@
#include <sourcelocationentry.h>
#include <sourcelocationscontainer.h>
#include <tokeninfos.h>
+#include <tooltipinfo.h>
#include <cpptools/usages.h>
@@ -269,6 +270,17 @@ std::ostream &operator<<(std::ostream &os, const ReferencesMessage &message)
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*/)
{
return os << "()";
@@ -385,7 +397,8 @@ std::ostream &operator<<(std::ostream &os, const FileContainer &container)
<< container.filePath() << ", "
<< container.projectPartId() << ", "
<< container.fileArguments() << ", "
- << container.documentRevision();
+ << container.documentRevision() << ", "
+ << container.textCodecName();
if (container.hasUnsavedFileContent())
os << ", "
@@ -552,6 +565,37 @@ std::ostream &operator<<(std::ostream &os, const RequestReferencesMessage &messa
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)
{
diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h
index e08b4b33c0b..d58daaf40c2 100644
--- a/tests/unit/unittest/gtest-creator-printing.h
+++ b/tests/unit/unittest/gtest-creator-printing.h
@@ -73,6 +73,7 @@ class CodeCompletedMessage;
class EchoMessage;
class DocumentAnnotationsChangedMessage;
class ReferencesMessage;
+class ToolTipMessage;
class FollowSymbolMessage;
class CompleteCodeMessage;
class EndMessage;
@@ -97,6 +98,7 @@ class RemovePchProjectPartsMessage;
class RequestDocumentAnnotationsMessage;
class RequestFollowSymbolMessage;
class RequestReferencesMessage;
+class RequestToolTipMessage;
class RequestSourceLocationsForRenamingMessage;
class RequestSourceRangesAndDiagnosticsForQueryMessage;
class RequestSourceRangesForQueryMessage;
@@ -114,6 +116,7 @@ class UpdateVisibleTranslationUnitsMessage;
class FilePath;
class TokenInfo;
class TokenInfos;
+class ToolTipInfo;
std::ostream &operator<<(std::ostream &out, const SourceLocationEntry &entry);
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 DocumentAnnotationsChangedMessage &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 CompleteCodeMessage &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 RequestFollowSymbolMessage &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 RequestSourceRangesAndDiagnosticsForQueryMessage &message);
std::ostream &operator<<(std::ostream &out, const RequestSourceRangesForQueryMessage &message);
diff --git a/tests/unit/unittest/mockclangcodemodelclient.h b/tests/unit/unittest/mockclangcodemodelclient.h
index 2e287909ddf..3b5a3cfbf5a 100644
--- a/tests/unit/unittest/mockclangcodemodelclient.h
+++ b/tests/unit/unittest/mockclangcodemodelclient.h
@@ -45,4 +45,6 @@ public:
void(const ClangBackEnd::ReferencesMessage &message));
MOCK_METHOD1(followSymbol,
void(const ClangBackEnd::FollowSymbolMessage &message));
+ MOCK_METHOD1(tooltip,
+ void(const ClangBackEnd::ToolTipMessage &message));
};
diff --git a/tests/unit/unittest/mockclangcodemodelserver.h b/tests/unit/unittest/mockclangcodemodelserver.h
index 8b6c66b2a33..468f683bf53 100644
--- a/tests/unit/unittest/mockclangcodemodelserver.h
+++ b/tests/unit/unittest/mockclangcodemodelserver.h
@@ -55,6 +55,8 @@ public:
void(const ClangBackEnd::RequestReferencesMessage &message));
MOCK_METHOD1(requestFollowSymbol,
void(const ClangBackEnd::RequestFollowSymbolMessage &message));
+ MOCK_METHOD1(requestToolTip,
+ void(const ClangBackEnd::RequestToolTipMessage &message));
MOCK_METHOD1(updateVisibleTranslationUnits,
void(const ClangBackEnd::UpdateVisibleTranslationUnitsMessage &message));
};
diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro
index a16a13e88bf..b6a698361ae 100644
--- a/tests/unit/unittest/unittest.pro
+++ b/tests/unit/unittest/unittest.pro
@@ -118,6 +118,7 @@ SOURCES += \
clangstring-test.cpp \
clangsupportivetranslationunitinitializer-test.cpp \
clangsuspenddocumentjob-test.cpp \
+ clangtooltipinfo-test.cpp \
clangtranslationunits-test.cpp \
clangtranslationunit-test.cpp \
clangupdatedocumentannotationsjob-test.cpp \
diff --git a/tests/unit/unittest/unsavedfile-test.cpp b/tests/unit/unittest/unsavedfile-test.cpp
index 05783a33c32..7e4d3dae685 100644
--- a/tests/unit/unittest/unsavedfile-test.cpp
+++ b/tests/unit/unittest/unsavedfile-test.cpp
@@ -203,4 +203,32 @@ TEST_F(UnsavedFile, HasCharacterForLastLineColumn)
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