diff options
author | Eike Ziller <eike.ziller@qt.io> | 2018-01-17 09:30:57 +0100 |
---|---|---|
committer | Eike Ziller <eike.ziller@qt.io> | 2018-01-17 09:30:57 +0100 |
commit | 115afed94b8e85db29b0c057e7c143a3d053d6cc (patch) | |
tree | 7663d95ed448c963ab8b913b675a78f0dea65fdc | |
parent | 76bc20ffceadfff894dfd98a0285ae52577be3bf (diff) | |
parent | 2765409d2bdc79753f96bf6bb894b2884ac5b0cd (diff) |
Merge remote-tracking branch 'origin/4.6'
Conflicts:
tests/unit/unittest/gtest-creator-printing.cpp
tests/unit/unittest/gtest-creator-printing.h
Change-Id: I43d2571617bfbf41c0fcf23502ab77975540eba4
113 files changed, 3769 insertions, 434 deletions
diff --git a/qbs/modules/qtc/qtc.qbs b/qbs/modules/qtc/qtc.qbs index 4b4796e63a7..0177c205643 100644 --- a/qbs/modules/qtc/qtc.qbs +++ b/qbs/modules/qtc/qtc.qbs @@ -18,14 +18,16 @@ Module { + ide_compat_version_minor + '.' + ide_compat_version_release property string qtcreator_copyright_year: '2017' + property string qtcreator_copyright_string: "(C) " + qtcreator_copyright_year + " The Qt Company Ltd" property string ide_display_name: 'Qt Creator' property string ide_id: 'qtcreator' property string ide_cased_id: 'QtCreator' + property string ide_bundle_identifier: 'org.qt-project.qtcreator' property string libDirName: "lib" property string ide_app_path: qbs.targetOS.contains("macos") ? "" : "bin" - property string ide_app_target: qbs.targetOS.contains("macos") ? "Qt Creator" : "qtcreator" + property string ide_app_target: qbs.targetOS.contains("macos") ? ide_display_name : ide_id property string ide_library_path: { if (qbs.targetOS.contains("macos")) return ide_app_target + ".app/Contents/Frameworks" diff --git a/qtcreator.pri b/qtcreator.pri index 09557fe15fd..8ef4e06e2b0 100644 --- a/qtcreator.pri +++ b/qtcreator.pri @@ -8,9 +8,11 @@ QTCREATOR_DISPLAY_VERSION = 4.6.0-beta1 QTCREATOR_COPYRIGHT_YEAR = 2017 BINARY_ARTIFACTS_BRANCH = master -isEmpty(IDE_DISPLAY_NAME): IDE_DISPLAY_NAME = Qt Creator -isEmpty(IDE_ID): IDE_ID = qtcreator -isEmpty(IDE_CASED_ID): IDE_CASED_ID = QtCreator +isEmpty(IDE_DISPLAY_NAME): IDE_DISPLAY_NAME = Qt Creator +isEmpty(IDE_ID): IDE_ID = qtcreator +isEmpty(IDE_CASED_ID): IDE_CASED_ID = QtCreator + +isEmpty(PRODUCT_BUNDLE_IDENTIFIER): PRODUCT_BUNDLE_IDENTIFIER = org.qt-project.$$IDE_ID CONFIG += c++14 @@ -100,7 +102,7 @@ isEmpty(IDE_BUILD_TREE) { IDE_APP_PATH = $$IDE_BUILD_TREE/bin osx { - IDE_APP_TARGET = "Qt Creator" + IDE_APP_TARGET = "$$IDE_DISPLAY_NAME" # check if IDE_BUILD_TREE is actually an existing Qt Creator.app, # for building against a binary package @@ -130,7 +132,7 @@ osx { INSTALL_APP_PATH = $$QTC_PREFIX/ } else { contains(TEMPLATE, vc.*):vcproj = 1 - IDE_APP_TARGET = qtcreator + IDE_APP_TARGET = $$IDE_ID # target output path if not set manually isEmpty(IDE_OUTPUT_PATH): IDE_OUTPUT_PATH = $$IDE_BUILD_TREE diff --git a/src/app/app-Info.plist b/src/app/app-Info.plist index 8940f2bd9ab..91104f163d5 100644 --- a/src/app/app-Info.plist +++ b/src/app/app-Info.plist @@ -245,9 +245,9 @@ <key>CFBundleSignature</key> <string>????</string> <key>CFBundleExecutable</key> - <string>Qt Creator</string> + <string>@EXECUTABLE@</string> <key>CFBundleIdentifier</key> - <string>org.qt-project.qtcreator</string> + <string>@PRODUCT_BUNDLE_IDENTIFIER@</string> <key>CFBundleVersion</key> <string>@FULL_VERSION@</string> <key>CFBundleShortVersionString</key> diff --git a/src/app/app.pro b/src/app/app.pro index f8abf4d7659..a64c357020d 100644 --- a/src/app/app.pro +++ b/src/app/app.pro @@ -62,6 +62,7 @@ win32 { infoplist = $$cat($$PWD/app-Info.plist, blob) infoplist = $$replace(infoplist, @MACOSX_DEPLOYMENT_TARGET@, $$QMAKE_MACOSX_DEPLOYMENT_TARGET) infoplist = $$replace(infoplist, @QTCREATOR_COPYRIGHT_YEAR@, $$QTCREATOR_COPYRIGHT_YEAR) + infoplist = $$replace(infoplist, @PRODUCT_BUNDLE_IDENTIFIER@, $$PRODUCT_BUNDLE_IDENTIFIER) write_file($$OUT_PWD/Info.plist, infoplist) QMAKE_INFO_PLIST = $$OUT_PWD/Info.plist diff --git a/src/app/app.qbs b/src/app/app.qbs index e3d9f027f0c..3ed4eb8fdef 100644 --- a/src/app/app.qbs +++ b/src/app/app.qbs @@ -25,6 +25,11 @@ QtcProduct { installTags: (isBundle ? ["bundle.content"] : base).concat(["debuginfo_app"]) property bool qtcRunnable: true + bundle.identifier: qtc.ide_bundle_identifier + bundle.infoPlist: ({ + "NSHumanReadableCopyright": qtc.qtcreator_copyright_string + }) + cpp.rpaths: qbs.targetOS.contains("macos") ? ["@executable_path/../Frameworks"] : ["$ORIGIN/../" + qtc.libDirName + "/qtcreator"] cpp.includePaths: [ 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/clangpathwatcher.h b/src/libs/clangsupport/clangpathwatcher.h index 127dc18e9c0..610e42cccf2 100644 --- a/src/libs/clangsupport/clangpathwatcher.h +++ b/src/libs/clangsupport/clangpathwatcher.h @@ -77,7 +77,7 @@ using IdCache = StringCache<Utils::SmallString, template <typename FileSystemWatcher, typename Timer> -class ClangPathWatcher : public ClangPathWatcherInterface +class CLANGSUPPORT_EXPORT ClangPathWatcher : public ClangPathWatcherInterface { public: ClangPathWatcher(FilePathCachingInterface &pathCache, 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 f8dc8c30117..34f65cdcad0 100644 --- a/src/libs/clangsupport/clangsupport_global.h +++ b/src/libs/clangsupport/clangsupport_global.h @@ -116,6 +116,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/filepathcache.h b/src/libs/clangsupport/filepathcache.h index 0e8fa7e4dfe..81bc3f40cc8 100644 --- a/src/libs/clangsupport/filepathcache.h +++ b/src/libs/clangsupport/filepathcache.h @@ -36,7 +36,7 @@ namespace ClangBackEnd { template <typename FilePathStorage> -class FilePathCache +class CLANGSUPPORT_EXPORT FilePathCache { using DirectoryPathCache = StringCache<Utils::PathString, int, 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/modelinglib/qmt/stereotype/stereotypecontroller.cpp b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp index 0db6b692467..5f70cb77da0 100644 --- a/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp +++ b/src/libs/modelinglib/qmt/stereotype/stereotypecontroller.cpp @@ -43,6 +43,47 @@ namespace qmt { +namespace { + +struct IconKey { + IconKey(StereotypeIcon::Element element, const QList<QString> &stereotypes, const QString &defaultIconPath, + const Uid &styleUid, const QSize &size, const QMarginsF &margins, qreal lineWidth) + : m_element(element), + m_stereotypes(stereotypes), + m_defaultIconPath(defaultIconPath), + m_styleUid(styleUid), + m_size(size), + m_margins(margins), + m_lineWidth(lineWidth) + { + } + + const StereotypeIcon::Element m_element; + const QList<QString> m_stereotypes; + const QString m_defaultIconPath; + const Uid m_styleUid; + const QSize m_size; + const QMarginsF m_margins; + const qreal m_lineWidth; +}; + +bool operator==(const IconKey &lhs, const IconKey &rhs) { + return lhs.m_element == rhs.m_element + && lhs.m_stereotypes == rhs.m_stereotypes + && lhs.m_defaultIconPath == rhs.m_defaultIconPath + && lhs.m_styleUid == rhs.m_styleUid + && lhs.m_size == rhs.m_size + && lhs.m_margins == rhs.m_margins + && lhs.m_lineWidth == rhs.m_lineWidth; +} + +uint qHash(const IconKey &key) { + return ::qHash(key.m_element) + qHash(key.m_stereotypes) + qHash(key.m_defaultIconPath) + + qHash(key.m_styleUid) + ::qHash(key.m_size.width()) + ::qHash(key.m_size.height()); +} + +} + class StereotypeController::StereotypeControllerPrivate { public: @@ -51,6 +92,7 @@ public: QHash<QString, CustomRelation> m_relationIdToCustomRelationMap; QList<Toolbar> m_toolbars; QList<Toolbar> m_elementToolbars; + QHash<IconKey, QIcon> m_iconMap; }; StereotypeController::StereotypeController(QObject *parent) : @@ -131,9 +173,10 @@ QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QL const QString &defaultIconPath, const Style *style, const QSize &size, const QMarginsF &margins, qreal lineWidth) { - // TODO implement cache with key build from element, stereotypes, defaultIconPath, style, size and margins - // TODO implement unique id for style which can be used as key - QIcon icon; + IconKey key(element, stereotypes, defaultIconPath, style->uid(), size, margins, lineWidth); + QIcon icon = d->m_iconMap.value(key); + if (!icon.isNull()) + return icon; QString stereotypeIconId = findStereotypeIconId(element, stereotypes); if (!stereotypeIconId.isEmpty()) { StereotypeIcon stereotypeIcon = findStereotypeIcon(stereotypeIconId); @@ -202,6 +245,7 @@ QIcon StereotypeController::createIcon(StereotypeIcon::Element element, const QL } if (icon.isNull() && !defaultIconPath.isEmpty()) icon = QIcon(defaultIconPath); + d->m_iconMap.insert(key, icon); return icon; } diff --git a/src/libs/modelinglib/qmt/style/style.h b/src/libs/modelinglib/qmt/style/style.h index 9add43d26e8..b2012e4f042 100644 --- a/src/libs/modelinglib/qmt/style/style.h +++ b/src/libs/modelinglib/qmt/style/style.h @@ -26,6 +26,7 @@ #pragma once #include "qmt/infrastructure/qmt_global.h" +#include "qmt/infrastructure/uid.h" #include <QString> #include <QPen> @@ -45,6 +46,7 @@ public: explicit Style(Type type); virtual ~Style(); + Uid uid() const { return m_uid; } Type type() const { return m_type; } QPen linePen() const { return m_linePen; } void setLinePen(const QPen &pen); @@ -68,6 +70,7 @@ public: void setHeaderFont(const QFont &font); private: + Uid m_uid; Type m_type; QPen m_linePen; QPen m_outerLinePen; diff --git a/src/libs/sqlite/utf8string.h b/src/libs/sqlite/utf8string.h index 17460ae82d4..cd1d6fdb550 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/autotest/qtest/qttestparser.cpp b/src/plugins/autotest/qtest/qttestparser.cpp index 1e885ac73a6..1bfabb42db8 100644 --- a/src/plugins/autotest/qtest/qttestparser.cpp +++ b/src/plugins/autotest/qtest/qttestparser.cpp @@ -271,6 +271,13 @@ static QtTestCodeLocationList tagLocationsFor(const QtTestParseResult *func, return QtTestCodeLocationList(); } +static bool isQObject(const CPlusPlus::Document::Ptr &declaringDoc) +{ + const QString file = declaringDoc->fileName(); + return (Utils::HostOsInfo::isMacHost() && file.endsWith("QtCore.framework/Headers/qobject.h")) + || file.endsWith("QtCore/qobject.h") || file.endsWith("kernel/qobject.h"); +} + static bool handleQtTest(QFutureInterface<TestParseResultPtr> futureInterface, CPlusPlus::Document::Ptr document, const CPlusPlus::Snapshot &snapshot, @@ -303,6 +310,10 @@ static bool handleQtTest(QFutureInterface<TestParseResultPtr> futureInterface, fetchAndMergeBaseTestFunctions( visitor.baseClasses(), testFunctions, declaringDoc, snapshot); + // handle tests that are not runnable without more information (plugin unit test of QC) + if (testFunctions.isEmpty() && testCaseName == "QObject" && isQObject(declaringDoc)) + return true; // we did not handle it, but we do not expect any test defined there either + const QSet<QString> &files = filesWithDataFunctionDefinitions(testFunctions); // TODO: change to QHash<> 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 ¶ms) 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 f73c5fe4e4a..9495e6d5ebc 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.cpp @@ -29,6 +29,7 @@ #include "clangeditordocumentprocessor.h" #include "clangutils.h" #include "clangfollowsymbol.h" +#include "clanghoverhandler.h" #include "clangrefactoringengine.h" #include <coreplugin/editormanager/editormanager.h> @@ -111,6 +112,11 @@ CppTools::CppCompletionAssistProvider *ModelManagerSupportClang::completionAssis return &m_completionAssistProvider; } +TextEditor::BaseHoverHandler *ModelManagerSupportClang::createHoverHandler() +{ + return new Internal::ClangHoverHandler; +} + CppTools::FollowSymbolInterface &ModelManagerSupportClang::followSymbolInterface() { return *m_followSymbol; @@ -121,7 +127,7 @@ CppTools::RefactoringEngineInterface &ModelManagerSupportClang::refactoringEngin return *m_refactoringEngine; } -CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::editorDocumentProcessor( +CppTools::BaseEditorDocumentProcessor *ModelManagerSupportClang::createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) { return new ClangEditorDocumentProcessor(m_communicator, baseTextDocument); diff --git a/src/plugins/clangcodemodel/clangmodelmanagersupport.h b/src/plugins/clangcodemodel/clangmodelmanagersupport.h index e87a7f162e8..92f253c5a8b 100644 --- a/src/plugins/clangcodemodel/clangmodelmanagersupport.h +++ b/src/plugins/clangcodemodel/clangmodelmanagersupport.h @@ -61,7 +61,8 @@ public: ~ModelManagerSupportClang(); CppTools::CppCompletionAssistProvider *completionAssistProvider() override; - CppTools::BaseEditorDocumentProcessor *editorDocumentProcessor( + TextEditor::BaseHoverHandler *createHoverHandler() override; + CppTools::BaseEditorDocumentProcessor *createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) override; CppTools::FollowSymbolInterface &followSymbolInterface() override; CppTools::RefactoringEngineInterface &refactoringEngineInterface() override; diff --git a/src/plugins/coreplugin/coreplugin.cpp b/src/plugins/coreplugin/coreplugin.cpp index 65fba9709f9..577a9f9487b 100644 --- a/src/plugins/coreplugin/coreplugin.cpp +++ b/src/plugins/coreplugin/coreplugin.cpp @@ -26,12 +26,13 @@ #include "coreplugin.h" #include "designmode.h" #include "editmode.h" -#include "idocument.h" #include "helpmanager.h" -#include "mainwindow.h" -#include "modemanager.h" +#include "idocument.h" #include "infobar.h" #include "iwizardfactory.h" +#include "mainwindow.h" +#include "menubarfilter.h" +#include "modemanager.h" #include "reaper_p.h" #include "themechooser.h" @@ -162,6 +163,8 @@ bool CorePlugin::initialize(const QStringList &arguments, QString *errorMessage) InfoBar::initialize(ICore::settings(), creatorTheme()); } + addAutoReleasedObject(new MenuBarFilter); + IWizardFactory::initialize(); // Make sure we respect the process's umask when creating new files diff --git a/src/plugins/coreplugin/coreplugin.pro b/src/plugins/coreplugin/coreplugin.pro index dd6f03bdaa7..d5f1f7f51ed 100644 --- a/src/plugins/coreplugin/coreplugin.pro +++ b/src/plugins/coreplugin/coreplugin.pro @@ -111,7 +111,8 @@ SOURCES += corejsextensions.cpp \ externaltoolmanager.cpp \ systemsettings.cpp \ coreicons.cpp \ - diffservice.cpp + diffservice.cpp \ + menubarfilter.cpp HEADERS += corejsextensions.h \ mainwindow.h \ @@ -222,7 +223,8 @@ HEADERS += corejsextensions.h \ systemsettings.h \ coreicons.h \ editormanager/documentmodel_p.h \ - diffservice.h + diffservice.h \ + menubarfilter.h FORMS += dialogs/newdialog.ui \ dialogs/saveitemsdialog.ui \ diff --git a/src/plugins/coreplugin/coreplugin.qbs b/src/plugins/coreplugin/coreplugin.qbs index eabf668df71..ce47658daad 100644 --- a/src/plugins/coreplugin/coreplugin.qbs +++ b/src/plugins/coreplugin/coreplugin.qbs @@ -32,79 +32,154 @@ Project { Group { name: "General" files: [ - "basefilewizard.cpp", "basefilewizard.h", - "basefilewizardfactory.cpp", "basefilewizardfactory.h", + "basefilewizard.cpp", + "basefilewizard.h", + "basefilewizardfactory.cpp", + "basefilewizardfactory.h", "core.qrc", "core_global.h", "coreconstants.h", - "coreicons.cpp", "coreicons.h", - "corejsextensions.cpp", "corejsextensions.h", - "coreplugin.cpp", "coreplugin.h", - "designmode.cpp", "designmode.h", - "diffservice.cpp", "diffservice.h", - "documentmanager.cpp", "documentmanager.h", - "editmode.cpp", "editmode.h", - "editortoolbar.cpp", "editortoolbar.h", - "externaltool.cpp", "externaltool.h", - "externaltoolmanager.cpp", "externaltoolmanager.h", - "fancyactionbar.cpp", "fancyactionbar.h", "fancyactionbar.qrc", - "fancytabwidget.cpp", "fancytabwidget.h", - "featureprovider.cpp", "featureprovider.h", - "fileiconprovider.cpp", "fileiconprovider.h", - "fileutils.cpp", "fileutils.h", - "findplaceholder.cpp", "findplaceholder.h", - "generalsettings.cpp", "generalsettings.h", "generalsettings.ui", - "generatedfile.cpp", "generatedfile.h", - "helpmanager.cpp", "helpmanager.h", - "icontext.cpp", "icontext.h", - "icore.cpp", "icore.h", - "id.cpp", "id.h", - "idocument.cpp", "idocument.h", - "idocumentfactory.cpp", "idocumentfactory.h", + "coreicons.cpp", + "coreicons.h", + "corejsextensions.cpp", + "corejsextensions.h", + "coreplugin.cpp", + "coreplugin.h", + "designmode.cpp", + "designmode.h", + "diffservice.cpp", + "diffservice.h", + "documentmanager.cpp", + "documentmanager.h", + "editmode.cpp", + "editmode.h", + "editortoolbar.cpp", + "editortoolbar.h", + "externaltool.cpp", + "externaltool.h", + "externaltoolmanager.cpp", + "externaltoolmanager.h", + "fancyactionbar.cpp", + "fancyactionbar.h", + "fancyactionbar.qrc", + "fancytabwidget.cpp", + "fancytabwidget.h", + "featureprovider.cpp", + "featureprovider.h", + "fileiconprovider.cpp", + "fileiconprovider.h", + "fileutils.cpp", + "fileutils.h", + "findplaceholder.cpp", + "findplaceholder.h", + "generalsettings.cpp", + "generalsettings.h", + "generalsettings.ui", + "generatedfile.cpp", + "generatedfile.h", + "helpmanager.cpp", + "helpmanager.h", + "icontext.cpp", + "icontext.h", + "icore.cpp", + "icore.h", + "id.cpp", + "id.h", + "idocument.cpp", + "idocument.h", + "idocumentfactory.cpp", + "idocumentfactory.h", "ifilewizardextension.h", - "imode.cpp", "imode.h", - "inavigationwidgetfactory.cpp", "inavigationwidgetfactory.h", - "infobar.cpp", "infobar.h", - "ioutputpane.cpp", "ioutputpane.h", - "iversioncontrol.cpp", "iversioncontrol.h", - "iwelcomepage.cpp", "iwelcomepage.h", - "iwizardfactory.cpp", "iwizardfactory.h", - "jsexpander.cpp", "jsexpander.h", - "mainwindow.cpp", "mainwindow.h", - "manhattanstyle.cpp", "manhattanstyle.h", - "messagebox.cpp", "messagebox.h", - "messagemanager.cpp", "messagemanager.h", - "messageoutputwindow.cpp", "messageoutputwindow.h", - "mimetypemagicdialog.cpp", "mimetypemagicdialog.h", "mimetypemagicdialog.ui", - "mimetypesettings.cpp", "mimetypesettings.h", + "imode.cpp", + "imode.h", + "inavigationwidgetfactory.cpp", + "inavigationwidgetfactory.h", + "infobar.cpp", + "infobar.h", + "ioutputpane.cpp", + "ioutputpane.h", + "iversioncontrol.cpp", + "iversioncontrol.h", + "iwelcomepage.cpp", + "iwelcomepage.h", + "iwizardfactory.cpp", + "iwizardfactory.h", + "jsexpander.cpp", + "jsexpander.h", + "mainwindow.cpp", + "mainwindow.h", + "manhattanstyle.cpp", + "manhattanstyle.h", + "menubarfilter.cpp", + "menubarfilter.h", + "messagebox.cpp", + "messagebox.h", + "messagemanager.cpp", + "messagemanager.h", + "messageoutputwindow.cpp", + "messageoutputwindow.h", + "mimetypemagicdialog.cpp", + "mimetypemagicdialog.h", + "mimetypemagicdialog.ui", + "mimetypesettings.cpp", + "mimetypesettings.h", "mimetypesettingspage.ui", - "minisplitter.cpp", "minisplitter.h", - "modemanager.cpp", "modemanager.h", - "navigationsubwidget.cpp", "navigationsubwidget.h", - "navigationwidget.cpp", "navigationwidget.h", - "opendocumentstreeview.cpp", "opendocumentstreeview.h", - "outputpane.cpp", "outputpane.h", - "outputpanemanager.cpp", "outputpanemanager.h", - "outputwindow.cpp", "outputwindow.h", - "patchtool.cpp", "patchtool.h", - "plugindialog.cpp", "plugindialog.h", - "reaper.cpp", "reaper.h", "reaper_p.h", - "rightpane.cpp", "rightpane.h", - "settingsdatabase.cpp", "settingsdatabase.h", - "shellcommand.cpp", "shellcommand.h", - "sidebar.cpp", "sidebar.h", - "sidebarwidget.cpp", "sidebarwidget.h", - "statusbarmanager.cpp", "statusbarmanager.h", - "statusbarwidget.cpp", "statusbarwidget.h", - "styleanimator.cpp", "styleanimator.h", - "systemsettings.cpp", "systemsettings.h", "systemsettings.ui", - "textdocument.cpp", "textdocument.h", - "themechooser.cpp", "themechooser.h", - "toolsettings.cpp", "toolsettings.h", - "variablechooser.cpp", "variablechooser.h", - "vcsmanager.cpp", "vcsmanager.h", - "versiondialog.cpp", "versiondialog.h", - "windowsupport.cpp", "windowsupport.h" + "minisplitter.cpp", + "minisplitter.h", + "modemanager.cpp", + "modemanager.h", + "navigationsubwidget.cpp", + "navigationsubwidget.h", + "navigationwidget.cpp", + "navigationwidget.h", + "opendocumentstreeview.cpp", + "opendocumentstreeview.h", + "outputpane.cpp", + "outputpane.h", + "outputpanemanager.cpp", + "outputpanemanager.h", + "outputwindow.cpp", + "outputwindow.h", + "patchtool.cpp", + "patchtool.h", + "plugindialog.cpp", + "plugindialog.h", + "reaper.cpp", + "reaper.h", + "reaper_p.h", + "rightpane.cpp", + "rightpane.h", + "settingsdatabase.cpp", + "settingsdatabase.h", + "shellcommand.cpp", + "shellcommand.h", + "sidebar.cpp", + "sidebar.h", + "sidebarwidget.cpp", + "sidebarwidget.h", + "statusbarmanager.cpp", + "statusbarmanager.h", + "statusbarwidget.cpp", + "statusbarwidget.h", + "styleanimator.cpp", + "styleanimator.h", + "systemsettings.cpp", + "systemsettings.h", + "systemsettings.ui", + "textdocument.cpp", + "textdocument.h", + "themechooser.cpp", + "themechooser.h", + "toolsettings.cpp", + "toolsettings.h", + "variablechooser.cpp", + "variablechooser.h", + "vcsmanager.cpp", + "vcsmanager.h", + "versiondialog.cpp", + "versiondialog.h", + "windowsupport.cpp", + "windowsupport.h", ] } @@ -268,6 +343,8 @@ Project { "filesystemfilter.ui", "ilocatorfilter.cpp", "ilocatorfilter.h", + "javascriptfilter.cpp", + "javascriptfilter.h", "locatorconstants.h", "locatorfiltersfilter.cpp", "locatorfiltersfilter.h", diff --git a/src/plugins/coreplugin/locator/javascriptfilter.cpp b/src/plugins/coreplugin/locator/javascriptfilter.cpp new file mode 100644 index 00000000000..02803ecf728 --- /dev/null +++ b/src/plugins/coreplugin/locator/javascriptfilter.cpp @@ -0,0 +1,123 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Andre Hartmann <aha_1980@gmx.de> +** 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 "javascriptfilter.h" + +#include <QClipboard> +#include <QGuiApplication> +#include <QJSEngine> + +namespace Core { +namespace Internal { + +JavaScriptFilter::JavaScriptFilter() +{ + setId("JavaScriptFilter"); + setDisplayName(tr("Evaluate JavaScript")); + setIncludedByDefault(false); + setShortcutString("="); +} + +JavaScriptFilter::~JavaScriptFilter() +{ +} + +void JavaScriptFilter::prepareSearch(const QString &entry) +{ + Q_UNUSED(entry); + + setupEngine(); +} + +QList<LocatorFilterEntry> JavaScriptFilter::matchesFor( + QFutureInterface<Core::LocatorFilterEntry> &future, const QString &entry) +{ + Q_UNUSED(future); + + const QString result = m_engine->evaluate(entry).toString(); + const QString expression = entry + " = " + result; + + QList<LocatorFilterEntry> entries; + entries.append({this, expression, QVariant()}); + entries.append({this, tr("Copy to clipboard: %1").arg(result), result}); + entries.append({this, tr("Copy to clipboard: %1").arg(expression), expression}); + + return entries; +} + +void JavaScriptFilter::accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const +{ + Q_UNUSED(newText); + Q_UNUSED(selectionStart); + Q_UNUSED(selectionLength); + + if (selection.internalData.isNull()) + return; + + QClipboard *clipboard = QGuiApplication::clipboard(); + clipboard->setText(selection.internalData.toString()); +} + +void JavaScriptFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); + // Nothing to refresh +} + +void JavaScriptFilter::setupEngine() +{ + if (m_engine) + return; + + m_engine = new QJSEngine(this); + m_engine->evaluate( + "function abs(x) { return Math.abs(x); }\n" + "function acos(x) { return Math.acos(x); }\n" + "function asin(x) { return Math.asin(x); }\n" + "function atan(x) { return Math.atan(x); }\n" + "function atan2(x, y) { return Math.atan2(x, y); }\n" + "function bin(x) { return '0b' + x.toString(2); }\n" + "function ceil(x) { return Math.ceil(x); }\n" + "function cos(x) { return Math.cos(x); }\n" + "function exp(x) { return Math.exp(x); }\n" + "function e() { return Math.E; }\n" + "function floor(x) { return Math.floor(x); }\n" + "function hex(x) { return '0x' + x.toString(16); }\n" + "function log(x) { return Math.log(x); }\n" + "function max(x, y) { return Math.max(x, y); }\n" + "function min(x, y) { return Math.min(x, y); }\n" + "function oct(x) { return '0' + x.toString(8); }\n" + "function pi() { return Math.PI; }\n" + "function pow(x, y) { return Math.pow(x, y); }\n" + "function random() { return Math.random(); }\n" + "function round(x) { return Math.round(x); }\n" + "function sin(x) { return Math.sin(x); }\n" + "function sqrt(x) { return Math.sqrt(x); }\n" + "function tan(x) { return Math.tan(x); }\n"); +} + +} // namespace Internal +} // namespace Core diff --git a/src/plugins/coreplugin/locator/javascriptfilter.h b/src/plugins/coreplugin/locator/javascriptfilter.h new file mode 100644 index 00000000000..50f5e85c3b1 --- /dev/null +++ b/src/plugins/coreplugin/locator/javascriptfilter.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2018 Andre Hartmann <aha_1980@gmx.de> +** 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 <coreplugin/locator/ilocatorfilter.h> + +QT_BEGIN_NAMESPACE +class QJSEngine; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class JavaScriptFilter : public Core::ILocatorFilter +{ + Q_OBJECT +public: + JavaScriptFilter(); + ~JavaScriptFilter(); + + virtual void prepareSearch(const QString &entry) override; + QList<Core::LocatorFilterEntry> matchesFor(QFutureInterface<Core::LocatorFilterEntry> &future, + const QString &entry) override; + void accept(Core::LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const override; + void refresh(QFutureInterface<void> &future) override; + +private: + void setupEngine(); + + QJSEngine *m_engine = nullptr; +}; + +} // namespace Internal +} // namespace Core diff --git a/src/plugins/coreplugin/locator/locator.cpp b/src/plugins/coreplugin/locator/locator.cpp index 6daab8026e8..e57f20241ac 100644 --- a/src/plugins/coreplugin/locator/locator.cpp +++ b/src/plugins/coreplugin/locator/locator.cpp @@ -27,6 +27,7 @@ #include "externaltoolsfilter.h" #include "filesystemfilter.h" +#include "javascriptfilter.h" #include "locatorconstants.h" #include "locatorfiltersfilter.h" #include "locatormanager.h" @@ -75,11 +76,13 @@ Locator::Locator() Locator::~Locator() { + m_corePlugin->removeObject(m_javaScriptFilter); m_corePlugin->removeObject(m_openDocumentsFilter); m_corePlugin->removeObject(m_fileSystemFilter); m_corePlugin->removeObject(m_executeFilter); m_corePlugin->removeObject(m_settingsPage); m_corePlugin->removeObject(m_externalToolsFilter); + delete m_javaScriptFilter; delete m_openDocumentsFilter; delete m_fileSystemFilter; delete m_executeFilter; @@ -119,6 +122,9 @@ void Locator::initialize(CorePlugin *corePlugin, const QStringList &, QString *) new LocatorManager(this); + m_javaScriptFilter = new JavaScriptFilter; + m_corePlugin->addObject(m_javaScriptFilter); + m_openDocumentsFilter = new OpenDocumentsFilter; m_corePlugin->addObject(m_openDocumentsFilter); diff --git a/src/plugins/coreplugin/locator/locator.h b/src/plugins/coreplugin/locator/locator.h index 325bcc11304..5d5d379a887 100644 --- a/src/plugins/coreplugin/locator/locator.h +++ b/src/plugins/coreplugin/locator/locator.h @@ -42,6 +42,7 @@ namespace Internal { class CorePlugin; class OpenDocumentsFilter; class FileSystemFilter; +class JavaScriptFilter; class LocatorSettingsPage; class ExternalToolsFilter; @@ -85,6 +86,7 @@ private: QList<ILocatorFilter *> m_customFilters; QMap<Id, QAction *> m_filterActionMap; QTimer m_refreshTimer; + JavaScriptFilter *m_javaScriptFilter = nullptr; OpenDocumentsFilter *m_openDocumentsFilter = nullptr; FileSystemFilter *m_fileSystemFilter = nullptr; ExecuteFilter *m_executeFilter = nullptr; diff --git a/src/plugins/coreplugin/locator/locator.pri b/src/plugins/coreplugin/locator/locator.pri index e80765bfc5f..183d7fde515 100644 --- a/src/plugins/coreplugin/locator/locator.pri +++ b/src/plugins/coreplugin/locator/locator.pri @@ -1,3 +1,5 @@ +QT *= qml + HEADERS += \ $$PWD/locator.h \ $$PWD/commandlocator.h \ @@ -13,7 +15,8 @@ HEADERS += \ $$PWD/executefilter.h \ $$PWD/locatorsearchutils.h \ $$PWD/locatorsettingspage.h \ - $$PWD/externaltoolsfilter.h + $$PWD/externaltoolsfilter.h \ + $$PWD/javascriptfilter.h SOURCES += \ $$PWD/locator.cpp \ @@ -29,7 +32,8 @@ SOURCES += \ $$PWD/executefilter.cpp \ $$PWD/locatorsearchutils.cpp \ $$PWD/locatorsettingspage.cpp \ - $$PWD/externaltoolsfilter.cpp + $$PWD/externaltoolsfilter.cpp \ + $$PWD/javascriptfilter.cpp FORMS += \ $$PWD/filesystemfilter.ui \ diff --git a/src/plugins/coreplugin/menubarfilter.cpp b/src/plugins/coreplugin/menubarfilter.cpp new file mode 100644 index 00000000000..5c3c4414db8 --- /dev/null +++ b/src/plugins/coreplugin/menubarfilter.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** 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 "menubarfilter.h" + +#include "actionmanager/actioncontainer.h" +#include "actionmanager/actionmanager.h" +#include "coreconstants.h" + +#include <utils/algorithm.h> +#include <utils/asconst.h> +#include <utils/qtcassert.h> +#include <utils/stringutils.h> + +#include <QMenuBar> +#include <QPointer> +#include <QRegularExpression> + +using namespace Core::Internal; +using namespace Core; + +MenuBarFilter::MenuBarFilter() +{ + setId("Actions from the menu"); + setDisplayName(tr("Actions from the Menu")); + setShortcutString("t"); +} + +static const QList<QAction *> menuBarActions() +{ + QMenuBar *menuBar = Core::ActionManager::actionContainer(Constants::MENU_BAR)->menuBar(); + QTC_ASSERT(menuBar, return {}); + return menuBar->actions(); +} + +QList<LocatorFilterEntry> MenuBarFilter::matchesFor(QFutureInterface<LocatorFilterEntry> &future, + const QString &entry) +{ + Q_UNUSED(future); + static const QString separators = ". >/"; + static const QRegularExpression seperatorRegExp(QString("[%1]").arg(separators)); + QList<LocatorFilterEntry> entries; + QString normalized = entry; + normalized.replace(seperatorRegExp, separators.at(0)); + const QStringList entryPath = normalized.split(separators.at(0), QString::SkipEmptyParts); + QVector<const QMenu *> processedMenus; + for (QAction* action : menuBarActions()) + entries << matchesForAction(action, entryPath, QStringList(), processedMenus); + + return entries; +} + +void MenuBarFilter::accept(LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const +{ + Q_UNUSED(newText); + Q_UNUSED(selectionStart); + Q_UNUSED(selectionLength); + if (auto action = selection.internalData.value<QPointer<QAction>>()) + action->trigger(); +} + +void MenuBarFilter::refresh(QFutureInterface<void> &future) +{ + Q_UNUSED(future); +} + +QList<LocatorFilterEntry> MenuBarFilter::matchesForAction(QAction *action, + const QStringList &entryPath, + const QStringList &path, + QVector<const QMenu *> &processedMenus) +{ + QList<LocatorFilterEntry> entries; + if (!action->isEnabled()) + return entries; + const QString text = Utils::stripAccelerator(action->text()); + if (QMenu *menu = action->menu()) { + if (processedMenus.contains(menu)) + return entries; + processedMenus.append(menu); + if (menu->isEnabled()) { + const QList<QAction *> &actions = menu->actions(); + QStringList menuPath(path); + menuPath << text; + for (QAction *menuAction : actions) + entries << matchesForAction(menuAction, entryPath, menuPath, processedMenus); + } + } else if (!text.isEmpty()) { + int entryIndex = 0; + int entryLength = 0; + int pathIndex = 0; + LocatorFilterEntry::HighlightInfo::DataType highlightType = + LocatorFilterEntry::HighlightInfo::DisplayName; + const QString pathText = path.join(" > "); + QStringList actionPath(path); + if (!entryPath.isEmpty()) { + actionPath << text; + for (const QString &entry : entryPath) { + const QRegularExpression re(".*" + entry + ".*", + QRegularExpression::CaseInsensitiveOption); + pathIndex = actionPath.indexOf(re, pathIndex); + if (pathIndex < 0) + return entries; + } + const QString &lastEntry(entryPath.last()); + entryLength = lastEntry.length(); + entryIndex = text.indexOf(lastEntry, 0, Qt::CaseInsensitive); + if (entryIndex >= 0) { + highlightType = LocatorFilterEntry::HighlightInfo::DisplayName; + } else { + entryIndex = pathText.indexOf(lastEntry, 0, Qt::CaseInsensitive); + QTC_ASSERT(entryIndex >= 0, return entries); + highlightType = LocatorFilterEntry::HighlightInfo::ExtraInfo; + } + } + LocatorFilterEntry filterEntry(this, text, QVariant(), action->icon()); + filterEntry.internalData.setValue(QPointer<QAction>(action)); + filterEntry.extraInfo = pathText; + filterEntry.highlightInfo = {entryIndex, entryLength, highlightType}; + entries << filterEntry; + } + return entries; +} + +static void requestMenuUpdate(const QAction* action) +{ + if (QMenu *menu = action->menu()) { + emit menu->aboutToShow(); + const QList<QAction *> &actions = menu->actions(); + for (const QAction *menuActions : actions) + requestMenuUpdate(menuActions); + } +} + +void Core::Internal::MenuBarFilter::prepareSearch(const QString &entry) +{ + Q_UNUSED(entry); + for (const QAction *action : menuBarActions()) + requestMenuUpdate(action); +} diff --git a/src/plugins/coreplugin/menubarfilter.h b/src/plugins/coreplugin/menubarfilter.h new file mode 100644 index 00000000000..9251df7da26 --- /dev/null +++ b/src/plugins/coreplugin/menubarfilter.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** 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 <coreplugin/locator/ilocatorfilter.h> + +QT_BEGIN_NAMESPACE +class QAction; +class QMenu; +QT_END_NAMESPACE + +namespace Core { +namespace Internal { + +class MenuBarFilter : public ILocatorFilter +{ +public: + MenuBarFilter(); + + QList<LocatorFilterEntry> matchesFor(QFutureInterface<LocatorFilterEntry> &future, + const QString &entry) override; + void accept(LocatorFilterEntry selection, QString *newText, + int *selectionStart, int *selectionLength) const override; + void refresh(QFutureInterface<void> &future) override; + void prepareSearch(const QString &entry) override; +private: + QList<LocatorFilterEntry> matchesForAction(QAction *action, + const QStringList &entryPath, + const QStringList &path, + QVector<const QMenu *> &processedMenus); + +}; + +} // namespace Internal +} // namespace Core diff --git a/src/plugins/cppeditor/cppeditor.pro b/src/plugins/cppeditor/cppeditor.pro index 0bc3391ce81..74b1daf63b3 100644 --- a/src/plugins/cppeditor/cppeditor.pro +++ b/src/plugins/cppeditor/cppeditor.pro @@ -12,10 +12,8 @@ HEADERS += \ cppeditorenums.h \ cppeditorplugin.h \ cppeditorwidget.h \ - cppelementevaluator.h \ cppfunctiondecldeflink.h \ cpphighlighter.h \ - cpphoverhandler.h \ cppparsecontext.h \ cppincludehierarchy.h \ cppinsertvirtualmethods.h \ @@ -38,10 +36,8 @@ SOURCES += \ cppeditordocument.cpp \ cppeditorplugin.cpp \ cppeditorwidget.cpp \ - cppelementevaluator.cpp \ cppfunctiondecldeflink.cpp \ cpphighlighter.cpp \ - cpphoverhandler.cpp \ cppparsecontext.cpp \ cppincludehierarchy.cpp \ cppinsertvirtualmethods.cpp \ diff --git a/src/plugins/cppeditor/cppeditor.qbs b/src/plugins/cppeditor/cppeditor.qbs index 5d8b17adb41..5a44f175eb2 100644 --- a/src/plugins/cppeditor/cppeditor.qbs +++ b/src/plugins/cppeditor/cppeditor.qbs @@ -39,14 +39,10 @@ QtcPlugin { "cppeditorenums.h", "cppeditorplugin.cpp", "cppeditorplugin.h", - "cppelementevaluator.cpp", - "cppelementevaluator.h", "cppfunctiondecldeflink.cpp", "cppfunctiondecldeflink.h", "cpphighlighter.cpp", "cpphighlighter.h", - "cpphoverhandler.cpp", - "cpphoverhandler.h", "cppincludehierarchy.cpp", "cppincludehierarchy.h", "cppinsertvirtualmethods.cpp", diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index f168f1810b3..3fcbda6a1ee 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -394,7 +394,7 @@ const MinimizableInfoBars &CppEditorDocument::minimizableInfoBars() const CppTools::BaseEditorDocumentProcessor *CppEditorDocument::processor() { if (!m_processor) { - m_processor.reset(mm()->editorDocumentProcessor(this)); + m_processor.reset(mm()->createEditorDocumentProcessor(this)); connect(m_processor.data(), &CppTools::BaseEditorDocumentProcessor::projectPartInfoUpdated, [this] (const CppTools::ProjectPartInfo &info) { diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 3f352f1ffe8..27cbf4827e5 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -31,7 +31,6 @@ #include "cppeditorwidget.h" #include "cppeditordocument.h" #include "cpphighlighter.h" -#include "cpphoverhandler.h" #include "cppincludehierarchy.h" #include "cppoutline.h" #include "cppquickfixassistant.h" @@ -53,6 +52,7 @@ #include <coreplugin/icore.h> #include <coreplugin/navigationwidget.h> #include <coreplugin/progressmanager/progressmanager.h> +#include <cpptools/cpphoverhandler.h> #include <cpptools/cpptoolsconstants.h> #include <texteditor/texteditoractionhandler.h> #include <texteditor/texteditorconstants.h> @@ -105,7 +105,7 @@ public: | TextEditorActionHandler::UnCollapseAll | TextEditorActionHandler::FollowSymbolUnderCursor); - addHoverHandler(new CppHoverHandler); + addHoverHandler(CppModelManager::instance()->createHoverHandler()); addHoverHandler(new ColorPreviewHoverHandler); addHoverHandler(new ResourcePreviewHoverHandler); } @@ -145,7 +145,6 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err { Q_UNUSED(errorMessage) - addAutoReleasedObject(new CppEditorFactory); addAutoReleasedObject(new CppOutlineWidgetFactory); addAutoReleasedObject(new CppTypeHierarchyFactory); addAutoReleasedObject(new CppIncludeHierarchyFactory); @@ -268,6 +267,10 @@ bool CppEditorPlugin::initialize(const QStringList & /*arguments*/, QString *err void CppEditorPlugin::extensionsInitialized() { + // Add the editor factory here instead of in initialize() + // so that the Clang Code Model has a chance to hook in. + addAutoReleasedObject(new CppEditorFactory); + if (!HostOsInfo::isMacHost() && !HostOsInfo::isWindowsHost()) { FileIconProvider::registerIconOverlayForMimeType( QIcon(creatorTheme()->imageFile(Theme::IconOverlayCppSource, QLatin1String(":/cppeditor/images/qt_cpp.png"))), diff --git a/src/plugins/cppeditor/cpphoverhandler.cpp b/src/plugins/cppeditor/cpphoverhandler.cpp deleted file mode 100644 index d480c12c61b..00000000000 --- a/src/plugins/cppeditor/cpphoverhandler.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 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 "cpphoverhandler.h" - -#include "cppeditorconstants.h" -#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; -} - -bool editorDocumentProcessorHasDiagnosticAt(TextEditorWidget *editorWidget, int pos) -{ - if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) { - int line, column; - if (Utils::Text::convertPosition(editorWidget->document(), pos, &line, &column)) - return processor->hasDiagnosticsAt(line, column); - } - - return false; -} - -void processWithEditorDocumentProcessor(TextEditorWidget *editorWidget, - const QPoint &point, - int position, - const QString &helpId) -{ - if (CppTools::BaseEditorDocumentProcessor *processor = editorDocumentProcessor(editorWidget)) { - int line, column; - if (Utils::Text::convertPosition(editorWidget->document(), position, &line, &column)) { - auto layout = new QVBoxLayout; - layout->setContentsMargins(0, 0, 0, 0); - layout->setSpacing(2); - processor->addDiagnosticToolTipToLayout(line, column, layout); - Utils::ToolTip::show(point, layout, editorWidget, helpId); - } - } -} - -} // anonymous namespace - -namespace CppEditor { -namespace Internal { - -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); - - CppElementEvaluator evaluator(editorWidget); - evaluator.setTextCursor(tc); - evaluator.execute(); - if (evaluator.hasDiagnosis()) { - setToolTip(evaluator.diagnosis()); - setPriority(Priority_Diagnostic); - } else if (evaluator.identifiedCppElement()) { - const QSharedPointer<CppElement> &cppElement = evaluator.cppElement(); - if (priority() != Priority_Diagnostic) { - setToolTip(cppElement->tooltip); - setPriority(cppElement->tooltip.isEmpty() ? Priority_None : Priority_Tooltip); - } - QStringList candidates = cppElement->helpIdCandidates; - candidates.removeDuplicates(); - foreach (const QString &helpId, candidates) { - if (helpId.isEmpty()) - continue; - - const QMap<QString, QUrl> helpLinks = HelpManager::linksForIdentifier(helpId); - if (!helpLinks.isEmpty()) { - setLastHelpItemIdentified(HelpItem(helpId, - cppElement->helpMark, - cppElement->helpCategory, - helpLinks)); - break; - } - } - } -} - -void CppHoverHandler::decorateToolTip() -{ - if (m_positionForEditorDocumentProcessor != -1) - return; - - if (Qt::mightBeRichText(toolTip())) - setToolTip(toolTip().toHtmlEscaped()); - - if (priority() != Priority_Diagnostic) - return; - - 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()); - } - } -} - -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 Internal -} // namespace CppEditor diff --git a/src/plugins/cppeditor/cppincludehierarchy.cpp b/src/plugins/cppeditor/cppincludehierarchy.cpp index 1f5f089181c..22bebf3dafa 100644 --- a/src/plugins/cppeditor/cppincludehierarchy.cpp +++ b/src/plugins/cppeditor/cppincludehierarchy.cpp @@ -29,13 +29,13 @@ #include "cppeditorwidget.h" #include "cppeditorconstants.h" #include "cppeditorplugin.h" -#include "cppelementevaluator.h" #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/fileiconprovider.h> #include <coreplugin/find/itemviewfind.h> #include <cpptools/baseeditordocumentprocessor.h> +#include <cpptools/cppelementevaluator.h> #include <cpptools/cppmodelmanager.h> #include <cpptools/cpptoolsbridge.h> #include <cpptools/editordocumenthandle.h> diff --git a/src/plugins/cppeditor/cpptypehierarchy.cpp b/src/plugins/cppeditor/cpptypehierarchy.cpp index 54eaaf2f57c..0b3198d4c9f 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.cpp +++ b/src/plugins/cppeditor/cpptypehierarchy.cpp @@ -28,11 +28,11 @@ #include "cppeditorconstants.h" #include "cppeditor.h" #include "cppeditorwidget.h" -#include "cppelementevaluator.h" #include "cppeditorplugin.h" #include <coreplugin/find/itemviewfind.h> #include <coreplugin/editormanager/editormanager.h> +#include <cpptools/cppelementevaluator.h> #include <utils/algorithm.h> #include <utils/annotateditemdelegate.h> #include <utils/navigationtreeview.h> @@ -46,6 +46,7 @@ #include <QVBoxLayout> using namespace CppEditor; +using namespace CppTools; using namespace CppEditor::Internal; using namespace Utils; diff --git a/src/plugins/cppeditor/cpptypehierarchy.h b/src/plugins/cppeditor/cpptypehierarchy.h index 355f87065d9..e99d8a180e8 100644 --- a/src/plugins/cppeditor/cpptypehierarchy.h +++ b/src/plugins/cppeditor/cpptypehierarchy.h @@ -47,11 +47,12 @@ class NavigationTreeView; class AnnotatedItemDelegate; } +namespace CppTools { class CppClass; } + namespace CppEditor { namespace Internal { class CppEditorWidget; -class CppClass; class CppTypeHierarchyModel : public QStandardItemModel { @@ -75,8 +76,8 @@ public: void perform(); private: - typedef QList<CppClass> CppClass::*HierarchyMember; - void buildHierarchy(const CppClass &cppClass, QStandardItem *parent, + typedef QList<CppTools::CppClass> CppTools::CppClass::*HierarchyMember; + void buildHierarchy(const CppTools::CppClass &cppClass, QStandardItem *parent, bool isRoot, HierarchyMember member); void showNoTypeHierarchyLabel(); void showTypeHierarchy(); diff --git a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp index 1f7576c49b1..ef025a61795 100644 --- a/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp +++ b/src/plugins/cppeditor/followsymbol_switchmethoddecldef_test.cpp @@ -27,8 +27,8 @@ #include "cppeditorwidget.h" #include "cppeditorplugin.h" #include "cppeditortestcase.h" -#include "cppelementevaluator.h" +#include <cpptools/cppelementevaluator.h> #include <cpptools/cppfollowsymbolundercursor.h> #include <cpptools/cppvirtualfunctionassistprovider.h> #include <cpptools/cppvirtualfunctionproposalitem.h> 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 ¶ms) = 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/cppeditor/cppelementevaluator.cpp b/src/plugins/cpptools/cppelementevaluator.cpp index 1e95850a18a..3281783ef26 100644 --- a/src/plugins/cppeditor/cppelementevaluator.cpp +++ b/src/plugins/cpptools/cppelementevaluator.cpp @@ -42,8 +42,7 @@ using namespace CPlusPlus; -namespace CppEditor { -namespace Internal { +namespace CppTools { static QStringList stripName(const QString &name) { @@ -471,5 +470,4 @@ CppEnumerator::CppEnumerator(EnumeratorDeclaration *declaration) tooltip.append(QLatin1String(" = ") + enumeratorValue); } -} // namespace Internal -} // namespace CppEditor +} // namespace CppTools diff --git a/src/plugins/cppeditor/cppelementevaluator.h b/src/plugins/cpptools/cppelementevaluator.h index 91ef68e2d89..af994989edc 100644 --- a/src/plugins/cppeditor/cppelementevaluator.h +++ b/src/plugins/cpptools/cppelementevaluator.h @@ -25,6 +25,8 @@ #pragma once +#include "cpptools_global.h" + #include <texteditor/texteditor.h> #include <texteditor/helpitem.h> @@ -41,14 +43,11 @@ class LookupItem; class LookupContext; } -namespace CppTools { class CppModelManager; } - -namespace CppEditor { -namespace Internal { - +namespace CppTools { class CppElement; +class CppModelManager; -class CppElementEvaluator +class CPPTOOLS_EXPORT CppElementEvaluator { public: explicit CppElementEvaluator(TextEditor::TextEditorWidget *editor); @@ -82,7 +81,7 @@ private: QString m_diagnosis; }; -class CppElement +class CPPTOOLS_EXPORT CppElement { protected: CppElement(); @@ -189,5 +188,4 @@ public: explicit CppEnumerator(CPlusPlus::EnumeratorDeclaration *declaration); }; -} // namespace Internal -} // namespace CppEditor +} // namespace CppTools diff --git a/src/plugins/cpptools/cpphoverhandler.cpp b/src/plugins/cpptools/cpphoverhandler.cpp new file mode 100644 index 00000000000..1ff41b7fdc3 --- /dev/null +++ b/src/plugins/cpptools/cpphoverhandler.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2016 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 "cpphoverhandler.h" + +#include "cppelementevaluator.h" + +#include <coreplugin/helpmanager.h> +#include <texteditor/texteditor.h> + +#include <utils/textutils.h> + +#include <QTextCursor> +#include <QUrl> + +using namespace Core; +using namespace TextEditor; + +namespace CppTools { + +QString CppHoverHandler::tooltipTextForHelpItem(const HelpItem &helpItem) +{ + // If Qt is built with a namespace, we still show the tip without it, as + // it is in the docs and for consistency with the doc extraction mechanism. + const HelpItem::Category category = helpItem.category(); + const QString &contents = helpItem.extractContent(false); + if (!contents.isEmpty()) { + if (category == HelpItem::ClassOrNamespace) + return helpItem.helpId() + contents; + else + return contents; + } else if (category == HelpItem::Typedef || + category == HelpItem::Enum || + category == HelpItem::ClassOrNamespace) { + // This approach is a bit limited since it cannot be used for functions + // because the help id doesn't really help in that case. + QString prefix; + if (category == HelpItem::Typedef) + prefix = QLatin1String("typedef "); + else if (category == HelpItem::Enum) + prefix = QLatin1String("enum "); + return prefix + helpItem.helpId(); + } + + return QString(); +} + +void CppHoverHandler::identifyMatch(TextEditorWidget *editorWidget, int pos) +{ + QTextCursor tc(editorWidget->document()); + tc.setPosition(pos); + + CppElementEvaluator evaluator(editorWidget); + evaluator.setTextCursor(tc); + evaluator.execute(); + if (evaluator.hasDiagnosis()) { + setToolTip(evaluator.diagnosis()); + setPriority(Priority_Diagnostic); + } else if (evaluator.identifiedCppElement()) { + const QSharedPointer<CppElement> &cppElement = evaluator.cppElement(); + if (priority() != Priority_Diagnostic) { + setToolTip(cppElement->tooltip); + setPriority(cppElement->tooltip.isEmpty() ? Priority_None : Priority_Tooltip); + } + QStringList candidates = cppElement->helpIdCandidates; + candidates.removeDuplicates(); + foreach (const QString &helpId, candidates) { + if (helpId.isEmpty()) + continue; + + const QMap<QString, QUrl> helpLinks = HelpManager::linksForIdentifier(helpId); + if (!helpLinks.isEmpty()) { + setLastHelpItemIdentified(HelpItem(helpId, + cppElement->helpMark, + cppElement->helpCategory, + helpLinks)); + break; + } + } + } +} + +void CppHoverHandler::decorateToolTip() +{ + if (Qt::mightBeRichText(toolTip())) + setToolTip(toolTip().toHtmlEscaped()); + + if (priority() == Priority_Diagnostic) + return; + + const HelpItem &help = lastHelpItemIdentified(); + if (help.isValid()) { + const QString text = tooltipTextForHelpItem(help); + if (!text.isEmpty()) + setToolTip(text); + } +} + +} // namespace CppTools diff --git a/src/plugins/cppeditor/cpphoverhandler.h b/src/plugins/cpptools/cpphoverhandler.h index accc2751523..edfa6901cf8 100644 --- a/src/plugins/cppeditor/cpphoverhandler.h +++ b/src/plugins/cpptools/cpphoverhandler.h @@ -25,21 +25,20 @@ #pragma once +#include "cpptools_global.h" + #include <texteditor/basehoverhandler.h> -namespace CppEditor { -namespace Internal { +namespace CppTools { -class CppHoverHandler : public TextEditor::BaseHoverHandler +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 Internal -} // namespace CppEditor +} // namespace CppTools diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index ad255459b2c..f1b1d2c2bd0 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -1293,10 +1293,15 @@ CppCompletionAssistProvider *CppModelManager::completionAssistProvider() const return d->m_activeModelManagerSupport->completionAssistProvider(); } -BaseEditorDocumentProcessor *CppModelManager::editorDocumentProcessor( - TextEditor::TextDocument *baseTextDocument) const +TextEditor::BaseHoverHandler *CppModelManager::createHoverHandler() const { - return d->m_activeModelManagerSupport->editorDocumentProcessor(baseTextDocument); + return d->m_activeModelManagerSupport->createHoverHandler(); +} + +BaseEditorDocumentProcessor *CppModelManager::createEditorDocumentProcessor( + TextEditor::TextDocument *baseTextDocument) const +{ + return d->m_activeModelManagerSupport->createEditorDocumentProcessor(baseTextDocument); } void CppModelManager::setIndexingSupport(CppIndexingSupport *indexingSupport) diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 3eb06eca653..d8b8d2f1922 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -44,7 +44,10 @@ class IEditor; } namespace CPlusPlus { class LookupContext; } namespace ProjectExplorer { class Project; } -namespace TextEditor { class TextDocument; } +namespace TextEditor { +class BaseHoverHandler; +class TextDocument; +} // namespace TextEditor namespace CppTools { @@ -172,8 +175,9 @@ public: void activateClangCodeModel(ModelManagerSupportProvider *modelManagerSupportProvider); CppCompletionAssistProvider *completionAssistProvider() const; - BaseEditorDocumentProcessor *editorDocumentProcessor( - TextEditor::TextDocument *baseTextDocument) const; + BaseEditorDocumentProcessor *createEditorDocumentProcessor( + TextEditor::TextDocument *baseTextDocument) const; + TextEditor::BaseHoverHandler *createHoverHandler() const; FollowSymbolInterface &followSymbolInterface() const; void setIndexingSupport(CppIndexingSupport *indexingSupport); diff --git a/src/plugins/cpptools/cppmodelmanagersupport.h b/src/plugins/cpptools/cppmodelmanagersupport.h index b1256aa51fa..06102f6007f 100644 --- a/src/plugins/cpptools/cppmodelmanagersupport.h +++ b/src/plugins/cpptools/cppmodelmanagersupport.h @@ -30,7 +30,10 @@ #include <QSharedPointer> #include <QString> -namespace TextEditor { class TextDocument; } +namespace TextEditor { +class TextDocument; +class BaseHoverHandler; +} // namespace TextEditor namespace CppTools { @@ -48,7 +51,8 @@ public: virtual ~ModelManagerSupport() = 0; virtual CppCompletionAssistProvider *completionAssistProvider() = 0; - virtual BaseEditorDocumentProcessor *editorDocumentProcessor( + virtual TextEditor::BaseHoverHandler *createHoverHandler() = 0; + virtual BaseEditorDocumentProcessor *createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) = 0; virtual FollowSymbolInterface &followSymbolInterface() = 0; virtual RefactoringEngineInterface &refactoringEngineInterface() = 0; diff --git a/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp b/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp index 95b98bbdf38..51919955549 100644 --- a/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp +++ b/src/plugins/cpptools/cppmodelmanagersupportinternal.cpp @@ -26,6 +26,7 @@ #include "cppcompletionassist.h" #include "cppmodelmanagersupportinternal.h" #include "cppfollowsymbolundercursor.h" +#include "cpphoverhandler.h" #include "cpprefactoringengine.h" #include "builtineditordocumentprocessor.h" @@ -63,7 +64,7 @@ ModelManagerSupportInternal::~ModelManagerSupportInternal() { } -BaseEditorDocumentProcessor *ModelManagerSupportInternal::editorDocumentProcessor( +BaseEditorDocumentProcessor *ModelManagerSupportInternal::createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) { return new BuiltinEditorDocumentProcessor(baseTextDocument); @@ -74,6 +75,11 @@ CppCompletionAssistProvider *ModelManagerSupportInternal::completionAssistProvid return m_completionAssistProvider.data(); } +TextEditor::BaseHoverHandler *ModelManagerSupportInternal::createHoverHandler() +{ + return new CppHoverHandler; +} + FollowSymbolInterface &ModelManagerSupportInternal::followSymbolInterface() { return *m_followSymbol; diff --git a/src/plugins/cpptools/cppmodelmanagersupportinternal.h b/src/plugins/cpptools/cppmodelmanagersupportinternal.h index 6ac2d4d022c..e2954792ea4 100644 --- a/src/plugins/cpptools/cppmodelmanagersupportinternal.h +++ b/src/plugins/cpptools/cppmodelmanagersupportinternal.h @@ -41,7 +41,8 @@ public: virtual ~ModelManagerSupportInternal(); CppCompletionAssistProvider *completionAssistProvider() final; - BaseEditorDocumentProcessor *editorDocumentProcessor( + TextEditor::BaseHoverHandler *createHoverHandler() final; + BaseEditorDocumentProcessor *createEditorDocumentProcessor( TextEditor::TextDocument *baseTextDocument) final; FollowSymbolInterface &followSymbolInterface() final; RefactoringEngineInterface &refactoringEngineInterface() final; diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro index 4dfc0ceeced..e16e3ec0bcd 100644 --- a/src/plugins/cpptools/cpptools.pro +++ b/src/plugins/cpptools/cpptools.pro @@ -32,11 +32,13 @@ HEADERS += \ cppdoxygen.h \ cppeditoroutline.h \ cppeditorwidgetinterface.h \ + cppelementevaluator.h \ cppfileiterationorder.h \ cppfilesettingspage.h \ cppfindreferences.h \ cppfollowsymbolundercursor.h \ cppfunctionsfilter.h \ + cpphoverhandler.h \ cppincludesfilter.h \ cppindexingsupport.h \ cpplocalsymbols.h \ @@ -125,11 +127,13 @@ SOURCES += \ cppcurrentdocumentfilter.cpp \ cppeditoroutline.cpp \ cppdoxygen.cpp \ + cppelementevaluator.cpp \ cppfileiterationorder.cpp \ cppfilesettingspage.cpp \ cppfindreferences.cpp \ cppfollowsymbolundercursor.cpp \ cppfunctionsfilter.cpp \ + cpphoverhandler.cpp \ cppincludesfilter.cpp \ cppindexingsupport.cpp \ cpplocalsymbols.cpp \ diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs index a510f5a21d6..a258ce18a30 100644 --- a/src/plugins/cpptools/cpptools.qbs +++ b/src/plugins/cpptools/cpptools.qbs @@ -90,6 +90,8 @@ Project { "cppeditoroutline.cpp", "cppeditoroutline.h", "cppeditorwidgetinterface.h", + "cppelementevaluator.cpp", + "cppelementevaluator.h", "cppfileiterationorder.cpp", "cppfileiterationorder.h", "cppfilesettingspage.cpp", @@ -101,6 +103,8 @@ Project { "cppfollowsymbolundercursor.h", "cppfunctionsfilter.cpp", "cppfunctionsfilter.h", + "cpphoverhandler.cpp", + "cpphoverhandler.h", "cppincludesfilter.cpp", "cppincludesfilter.h", "cppindexingsupport.cpp", diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index a71127e38f0..e0fa782c390 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -3179,7 +3179,8 @@ QWidget *mainWindow() void raiseWatchersWindow() { - return dd->m_mainWindow->raiseDock(DOCKWIDGET_WATCHERS); + if (currentEngine()->state() != DebuggerNotReady) + dd->m_mainWindow->raiseDock(DOCKWIDGET_WATCHERS); } bool isRegistersWindowVisible() diff --git a/src/plugins/modeleditor/componentviewcontroller.cpp b/src/plugins/modeleditor/componentviewcontroller.cpp index 1c35e912021..52501649cd8 100644 --- a/src/plugins/modeleditor/componentviewcontroller.cpp +++ b/src/plugins/modeleditor/componentviewcontroller.cpp @@ -133,6 +133,7 @@ private: private: qmt::ModelController *m_modelController = nullptr; QMultiHash<QString, Node> m_filePaths; + QHash<QString, qmt::MComponent *> m_filePathComponentsMap; }; void UpdateIncludeDependenciesVisitor::setModelController(qmt::ModelController *modelController) @@ -161,6 +162,16 @@ void UpdateIncludeDependenciesVisitor::visitMComponent(qmt::MComponent *componen if (document) { foreach (const CPlusPlus::Document::Include &include, document->resolvedIncludes()) { QString includeFilePath = include.resolvedFileName(); + // replace proxy header with real one + CPlusPlus::Document::Ptr includeDocument = snapshot.document(includeFilePath); + if (includeDocument) { + QList<CPlusPlus::Document::Include> includes = includeDocument->resolvedIncludes(); + if (includes.count() == 1 && + QFileInfo(includes.at(0).resolvedFileName()).fileName() == QFileInfo(includeFilePath).fileName()) + { + includeFilePath = includes.at(0).resolvedFileName(); + } + } qmt::MComponent *includeComponent = findComponentFromFilePath(includeFilePath); if (includeComponent && includeComponent != component) { // add dependency between components @@ -312,10 +323,16 @@ void UpdateIncludeDependenciesVisitor::collectElementPaths(const ProjectExplorer qmt::MComponent *UpdateIncludeDependenciesVisitor::findComponentFromFilePath(const QString &filePath) { + const auto it = m_filePathComponentsMap.find(filePath); + if (it != m_filePathComponentsMap.cend()) + return it.value(); + FindComponentFromFilePath visitor; visitor.setFilePath(filePath); m_modelController->rootPackage()->accept(&visitor); - return visitor.component(); + qmt::MComponent *component = visitor.component(); + m_filePathComponentsMap.insert(filePath, component); + return component; } bool UpdateIncludeDependenciesVisitor::haveDependency(const qmt::MObject *source, @@ -390,7 +407,8 @@ void ComponentViewController::createComponentModel(const QString &filePath, const QString &anchorFolder) { d->diagramSceneController->modelController()->startResetModel(); - doCreateComponentModel(filePath, diagram, anchorFolder); + doCreateComponentModel(filePath, diagram, anchorFolder, false); + doCreateComponentModel(filePath, diagram, anchorFolder, true); d->diagramSceneController->modelController()->finishResetModel(true); } @@ -404,7 +422,8 @@ void ComponentViewController::updateIncludeDependencies(qmt::MPackage *rootPacka d->diagramSceneController->modelController()->finishResetModel(true); } -void ComponentViewController::doCreateComponentModel(const QString &filePath, qmt::MDiagram *diagram, const QString &anchorFolder) +void ComponentViewController::doCreateComponentModel(const QString &filePath, qmt::MDiagram *diagram, + const QString &anchorFolder, bool scanHeaders) { for (const QString &fileName : QDir(filePath).entryList(QDir::Files)) { QString file = filePath + "/" + fileName; @@ -413,18 +432,20 @@ void ComponentViewController::doCreateComponentModel(const QString &filePath, qm bool isSource = false; CppTools::ProjectFile::Kind kind = CppTools::ProjectFile::classify(file); switch (kind) { - case CppTools::ProjectFile::AmbiguousHeader: - case CppTools::ProjectFile::CHeader: case CppTools::ProjectFile::CSource: - case CppTools::ProjectFile::CXXHeader: case CppTools::ProjectFile::CXXSource: case CppTools::ProjectFile::ObjCSource: - case CppTools::ProjectFile::ObjCHeader: case CppTools::ProjectFile::ObjCXXSource: - case CppTools::ProjectFile::ObjCXXHeader: case CppTools::ProjectFile::CudaSource: case CppTools::ProjectFile::OpenCLSource: - isSource = true; + isSource = !scanHeaders; + break; + case CppTools::ProjectFile::AmbiguousHeader: + case CppTools::ProjectFile::CHeader: + case CppTools::ProjectFile::CXXHeader: + case CppTools::ProjectFile::ObjCHeader: + case CppTools::ProjectFile::ObjCXXHeader: + isSource = scanHeaders && !isProxyHeader(file); break; case CppTools::ProjectFile::Unclassified: case CppTools::ProjectFile::Unsupported: @@ -450,9 +471,24 @@ void ComponentViewController::doCreateComponentModel(const QString &filePath, qm } for (const QString &fileName : QDir(filePath).entryList(QDir::Dirs|QDir::NoDotAndDotDot)) { QString file = filePath + "/" + fileName; - doCreateComponentModel(file, diagram, anchorFolder); + doCreateComponentModel(file, diagram, anchorFolder, scanHeaders); } } +bool ComponentViewController::isProxyHeader(const QString &file) const +{ + CppTools::CppModelManager *cppModelManager = CppTools::CppModelManager::instance(); + CPlusPlus::Snapshot snapshot = cppModelManager->snapshot(); + + CPlusPlus::Document::Ptr document = snapshot.document(file); + if (document) { + QList<CPlusPlus::Document::Include> includes = document->resolvedIncludes(); + if (includes.count() != 1) + return false; + return QFileInfo(includes.at(0).resolvedFileName()).fileName() == QFileInfo(file).fileName(); + } + return false; +} + } // namespace Internal } // namespace ModelEditor diff --git a/src/plugins/modeleditor/componentviewcontroller.h b/src/plugins/modeleditor/componentviewcontroller.h index 98f4ac0ca6f..584bc55b50d 100644 --- a/src/plugins/modeleditor/componentviewcontroller.h +++ b/src/plugins/modeleditor/componentviewcontroller.h @@ -58,8 +58,9 @@ public: void updateIncludeDependencies(qmt::MPackage *rootPackage); private: - void doCreateComponentModel(const QString &filePath, - qmt::MDiagram *diagram, const QString &anchorFolder); + void doCreateComponentModel(const QString &filePath, qmt::MDiagram *diagram, + const QString &anchorFolder, bool scanHeaders); + bool isProxyHeader(const QString &file) const; ComponentViewControllerPrivate *d; }; diff --git a/src/plugins/qtsupport/baseqtversion.cpp b/src/plugins/qtsupport/baseqtversion.cpp index d8806023fb0..f2286bdc3c6 100644 --- a/src/plugins/qtsupport/baseqtversion.cpp +++ b/src/plugins/qtsupport/baseqtversion.cpp @@ -382,15 +382,15 @@ QString BaseQtVersion::defaultUnexpandedDisplayName(const FileName &qmakePath, b QDir dir = qmakePath.toFileInfo().absoluteDir(); do { const QString dirName = dir.dirName(); - if (dirName == QLatin1String("usr")) { // System-installed Qt. + if (dirName == "usr") { // System-installed Qt. location = QCoreApplication::translate("QtVersion", "System"); break; } location = dirName; // Also skip default checkouts named 'qt'. Parent dir might have descriptive name. - if (dirName.compare(QLatin1String("bin"), Qt::CaseInsensitive) - && dirName.compare(QLatin1String("qtbase"), Qt::CaseInsensitive) - && dirName.compare(QLatin1String("qt"), Qt::CaseInsensitive)) { + if (dirName.compare("bin", Qt::CaseInsensitive) + && dirName.compare("qtbase", Qt::CaseInsensitive) + && dirName.compare("qt", Qt::CaseInsensitive)) { break; } } while (!dir.isRoot() && dir.cdUp()); @@ -527,7 +527,7 @@ QList<Task> BaseQtVersion::validateKit(const Kit *k) QString qtAbiString; foreach (const Abi &qtAbi, qtAbis) { if (!qtAbiString.isEmpty()) - qtAbiString.append(QLatin1Char(' ')); + qtAbiString.append(' '); qtAbiString.append(qtAbi.toString()); if (!fullMatch) @@ -589,13 +589,13 @@ FileName BaseQtVersion::mkspecsPath() const if (result.isEmpty()) result = FileName::fromUserInput(qmakeProperty("QMAKE_MKSPECS")); else - result.appendPath(QLatin1String("mkspecs")); + result.appendPath("mkspecs"); return result; } FileName BaseQtVersion::qmlBinPath() const { - return FileName::fromUserInput(m_mkspecValues.value(QLatin1String("QT.qml.bins"))); + return FileName::fromUserInput(m_mkspecValues.value("QT.qml.bins")); } FileName BaseQtVersion::librarySearchPath() const @@ -623,13 +623,13 @@ FileNameList BaseQtVersion::directoriesToIgnoreInProjectTree() const QString BaseQtVersion::qtNamespace() const { ensureMkSpecParsed(); - return m_mkspecValues.value(QLatin1String(MKSPEC_VALUE_NAMESPACE)); + return m_mkspecValues.value(MKSPEC_VALUE_NAMESPACE); } QString BaseQtVersion::qtLibInfix() const { ensureMkSpecParsed(); - return m_mkspecValues.value(QLatin1String(MKSPEC_VALUE_LIBINFIX)); + return m_mkspecValues.value(MKSPEC_VALUE_LIBINFIX); } bool BaseQtVersion::isFrameworkBuild() const @@ -655,15 +655,15 @@ void BaseQtVersion::setId(int id) void BaseQtVersion::fromMap(const QVariantMap &map) { - m_id = map.value(QLatin1String(Constants::QTVERSIONID)).toInt(); + m_id = map.value(Constants::QTVERSIONID).toInt(); if (m_id == -1) // this happens on adding from installer, see updateFromInstaller => get a new unique id m_id = QtVersionManager::getUniqueId(); - m_unexpandedDisplayName = map.value(QLatin1String(Constants::QTVERSIONNAME)).toString(); - m_isAutodetected = map.value(QLatin1String(QTVERSIONAUTODETECTED)).toBool(); + m_unexpandedDisplayName = map.value(Constants::QTVERSIONNAME).toString(); + m_isAutodetected = map.value(QTVERSIONAUTODETECTED).toBool(); if (m_isAutodetected) - m_autodetectionSource = map.value(QLatin1String(QTVERSIONAUTODETECTIONSOURCE)).toString(); - QString string = map.value(QLatin1String(QTVERSIONQMAKEPATH)).toString(); - if (string.startsWith(QLatin1Char('~'))) + m_autodetectionSource = map.value(QTVERSIONAUTODETECTIONSOURCE).toString(); + QString string = map.value(QTVERSIONQMAKEPATH).toString(); + if (string.startsWith('~')) string.remove(0, 1).prepend(QDir::homePath()); m_qtSources = Utils::FileName::fromUserInput( @@ -683,12 +683,12 @@ void BaseQtVersion::fromMap(const QVariantMap &map) QVariantMap BaseQtVersion::toMap() const { QVariantMap result; - result.insert(QLatin1String(Constants::QTVERSIONID), uniqueId()); - result.insert(QLatin1String(Constants::QTVERSIONNAME), unexpandedDisplayName()); - result.insert(QLatin1String(QTVERSIONAUTODETECTED), isAutodetected()); + result.insert(Constants::QTVERSIONID, uniqueId()); + result.insert(Constants::QTVERSIONNAME, unexpandedDisplayName()); + result.insert(QTVERSIONAUTODETECTED, isAutodetected()); if (isAutodetected()) - result.insert(QLatin1String(QTVERSIONAUTODETECTIONSOURCE), autodetectionSource()); - result.insert(QLatin1String(QTVERSIONQMAKEPATH), qmakeCommand().toString()); + result.insert(QTVERSIONAUTODETECTIONSOURCE, autodetectionSource()); + result.insert(QTVERSIONQMAKEPATH, qmakeCommand().toString()); return result; } @@ -858,16 +858,16 @@ QString BaseQtVersion::toHtml(bool verbose) const foreach (const ProKey &key, keys) { const QString &value = vInfo.value(key).toQString(); QString variableName = key.toQString(); - if (variableName != QLatin1String("QMAKE_MKSPECS") - && !variableName.endsWith(QLatin1String("/raw"))) { + if (variableName != "QMAKE_MKSPECS" + && !variableName.endsWith("/raw")) { bool isPath = false; - if (variableName.contains(QLatin1String("_HOST_")) - || variableName.contains(QLatin1String("_INSTALL_"))) { - if (!variableName.endsWith(QLatin1String("/get"))) + if (variableName.contains("_HOST_") + || variableName.contains("_INSTALL_")) { + if (!variableName.endsWith("/get")) continue; variableName.chop(4); isPath = true; - } else if (variableName == QLatin1String("QT_SYSROOT")) { + } else if (variableName == "QT_SYSROOT") { isPath = true; } str << "<tr><td><pre>" << variableName << "</pre></td><td>"; @@ -946,7 +946,7 @@ QString BaseQtVersion::findHostBinary(HostBinaries binary) const switch (binary) { case Designer: case Linguist: - baseDir = m_mkspecValues.value(QLatin1String("QT.designer.bins")); + baseDir = m_mkspecValues.value("QT.designer.bins"); break; case Uic: case QScxmlc: @@ -960,33 +960,32 @@ QString BaseQtVersion::findHostBinary(HostBinaries binary) const if (baseDir.isEmpty()) return QString(); - if (!baseDir.endsWith(QLatin1Char('/'))) - baseDir += QLatin1Char('/'); + if (!baseDir.endsWith('/')) + baseDir += '/'; QStringList possibleCommands; switch (binary) { case Designer: if (HostOsInfo::isMacHost()) - possibleCommands << QLatin1String("Designer.app/Contents/MacOS/Designer"); + possibleCommands << "Designer.app/Contents/MacOS/Designer"; else - possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("designer")); + possibleCommands << HostOsInfo::withExecutableSuffix("designer"); break; case Linguist: if (HostOsInfo::isMacHost()) - possibleCommands << QLatin1String("Linguist.app/Contents/MacOS/Linguist"); + possibleCommands << "Linguist.app/Contents/MacOS/Linguist"; else - possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("linguist")); + possibleCommands << HostOsInfo::withExecutableSuffix("linguist"); break; case Uic: if (HostOsInfo::isWindowsHost()) { - possibleCommands << QLatin1String("uic.exe"); + possibleCommands << "uic.exe"; } else { - possibleCommands << QLatin1String("uic-qt4") << QLatin1String("uic4") - << QLatin1String("uic"); + possibleCommands << "uic-qt4" << "uic4" << "uic"; } break; case QScxmlc: - possibleCommands << HostOsInfo::withExecutableSuffix(QLatin1String("qscxmlc")); + possibleCommands << HostOsInfo::withExecutableSuffix("qscxmlc"); break; default: Q_ASSERT(false); @@ -1027,7 +1026,7 @@ void BaseQtVersion::updateMkspec() const m_mkspec = m_mkspec.relativeChildPath(baseMkspecDir); // qDebug() << "Setting mkspec to"<<mkspec; } else { - FileName sourceMkSpecPath = sourcePath().appendPath(QLatin1String("mkspecs")); + FileName sourceMkSpecPath = sourcePath().appendPath("mkspecs"); if (m_mkspec.isChildOf(sourceMkSpecPath)) { m_mkspec = m_mkspec.relativeChildPath(sourceMkSpecPath); } else { @@ -1062,25 +1061,25 @@ void BaseQtVersion::ensureMkSpecParsed() const void BaseQtVersion::parseMkSpec(ProFileEvaluator *evaluator) const { - m_configValues = evaluator->values(QLatin1String("CONFIG")); - m_qtConfigValues = evaluator->values(QLatin1String("QT_CONFIG")); + m_configValues = evaluator->values("CONFIG"); + m_qtConfigValues = evaluator->values("QT_CONFIG"); m_defaultConfigIsDebugAndRelease = false; m_frameworkBuild = false; foreach (const QString &value, m_configValues) { - if (value == QLatin1String("debug")) + if (value == "debug") m_defaultConfigIsDebug = true; - else if (value == QLatin1String("release")) + else if (value == "release") m_defaultConfigIsDebug = false; - else if (value == QLatin1String("build_all")) + else if (value == "build_all") m_defaultConfigIsDebugAndRelease = true; - else if (value == QLatin1String("qt_framework")) + else if (value == "qt_framework") m_frameworkBuild = true; } - const QString designerBins = QLatin1String("QT.designer.bins"); - const QString qmlBins = QLatin1String("QT.qml.bins"); - const QString declarativeBins = QLatin1String("QT.declarative.bins"); - const QString libinfix = QLatin1String(MKSPEC_VALUE_LIBINFIX); - const QString ns = QLatin1String(MKSPEC_VALUE_NAMESPACE); + const QString designerBins = "QT.designer.bins"; + const QString qmlBins = "QT.qml.bins"; + const QString declarativeBins = "QT.declarative.bins"; + const QString libinfix = MKSPEC_VALUE_LIBINFIX; + const QString ns = MKSPEC_VALUE_NAMESPACE; m_mkspecValues.insert(designerBins, evaluator->value(designerBins)); m_mkspecValues.insert(qmlBins, evaluator->value(qmlBins)); m_mkspecValues.insert(declarativeBins, evaluator->value(declarativeBins)); @@ -1123,11 +1122,11 @@ bool BaseQtVersion::hasMkspec(const FileName &spec) const return true; // default spec of a Qt version QDir mkspecDir = QDir(QDir::fromNativeSeparators(qmakeProperty("QT_HOST_DATA")) - + QLatin1String("/mkspecs/")); + + "/mkspecs/"); const QString absSpec = mkspecDir.absoluteFilePath(spec.toString()); if (QFileInfo(absSpec).isDir() && QFileInfo(absSpec + "/qmake.conf").isFile()) return true; - mkspecDir.setPath(sourcePath().toString() + QLatin1String("/mkspecs/")); + mkspecDir.setPath(sourcePath().toString() + "/mkspecs/"); const QString absSrcSpec = mkspecDir.absoluteFilePath(spec.toString()); return absSrcSpec != absSpec && QFileInfo(absSrcSpec).isDir() @@ -1239,7 +1238,7 @@ QString BaseQtVersion::qmakeProperty(const QHash<ProKey,ProString> &versionInfo, variant == PropertyVariantGet ? "/get" : "/src")))).toQString(); if (!val.isNull()) return val; - return versionInfo.value(ProKey(QString::fromLatin1(name))).toQString(); + return versionInfo.value(ProKey(name)).toQString(); } QString BaseQtVersion::qmakeProperty(const QByteArray &name, PropertyVariant variant) const @@ -1363,7 +1362,7 @@ void BaseQtVersion::populateQmlFileFinder(FileInProjectFinder *finder, const Tar void BaseQtVersion::addToEnvironment(const Kit *k, Environment &env) const { Q_UNUSED(k); - env.set(QLatin1String("QTDIR"), QDir::toNativeSeparators(qmakeProperty("QT_HOST_DATA"))); + env.set("QTDIR", QDir::toNativeSeparators(qmakeProperty("QT_HOST_DATA"))); } // Some Qt versions may require environment settings for qmake to work @@ -1454,7 +1453,7 @@ static QByteArray runQmakeQuery(const FileName &binary, const Environment &env, QProcess process; process.setEnvironment(env.toStringList()); - process.start(binary.toString(), QStringList(QLatin1String("-query")), QIODevice::ReadOnly); + process.start(binary.toString(), QStringList("-query"), QIODevice::ReadOnly); if (!process.waitForStarted()) { *error = QCoreApplication::translate("QtVersion", "Cannot start \"%1\": %2").arg(binary.toUserOutput()).arg(process.errorString()); @@ -1521,7 +1520,7 @@ FileName BaseQtVersion::mkspecDirectoryFromVersionInfo(const QHash<ProKey, ProSt QString dataDir = qmakeProperty(versionInfo, "QT_HOST_DATA", PropertyVariantSrc); if (dataDir.isEmpty()) return FileName(); - return FileName::fromUserInput(dataDir + QLatin1String("/mkspecs")); + return FileName::fromUserInput(dataDir + "/mkspecs"); } FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &versionInfo) @@ -1533,7 +1532,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &ve bool qt5 = false; QString theSpec = qmakeProperty(versionInfo, "QMAKE_XSPEC"); if (theSpec.isEmpty()) - theSpec = QLatin1String("default"); + theSpec = "default"; else qt5 = true; @@ -1544,7 +1543,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &ve if (HostOsInfo::isWindowsHost()) { if (!qt5) { - QFile f2(mkspecFullPath.toString() + QLatin1String("/qmake.conf")); + QFile f2(mkspecFullPath.toString() + "/qmake.conf"); if (f2.exists() && f2.open(QIODevice::ReadOnly)) { while (!f2.atEnd()) { QByteArray line = f2.readLine(); @@ -1552,16 +1551,16 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &ve const QList<QByteArray> &temp = line.split('='); if (temp.size() == 2) { QString possibleFullPath = QString::fromLocal8Bit(temp.at(1).trimmed().constData()); - if (possibleFullPath.contains(QLatin1Char('$'))) { // QTBUG-28792 - const QRegularExpression rex(QLatin1String("\\binclude\\(([^)]+)/qmake\\.conf\\)")); + if (possibleFullPath.contains('$')) { // QTBUG-28792 + const QRegularExpression rex("\\binclude\\(([^)]+)/qmake\\.conf\\)"); const QRegularExpressionMatch match = rex.match(QString::fromLocal8Bit(f2.readAll())); if (match.hasMatch()) { - possibleFullPath = mkspecFullPath.toString() + QLatin1Char('/') + possibleFullPath = mkspecFullPath.toString() + '/' + match.captured(1); } } // We sometimes get a mix of different slash styles here... - possibleFullPath = possibleFullPath.replace(QLatin1Char('\\'), QLatin1Char('/')); + possibleFullPath = possibleFullPath.replace('\\', '/'); if (QFileInfo::exists(possibleFullPath)) // Only if the path exists mkspecFullPath = FileName::fromUserInput(possibleFullPath); } @@ -1573,7 +1572,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &ve } } else { if (HostOsInfo::isMacHost()) { - QFile f2(mkspecFullPath.toString() + QLatin1String("/qmake.conf")); + QFile f2(mkspecFullPath.toString() + "/qmake.conf"); if (f2.exists() && f2.open(QIODevice::ReadOnly)) { while (!f2.atEnd()) { QByteArray line = f2.readLine(); @@ -1584,7 +1583,7 @@ FileName BaseQtVersion::mkspecFromVersionInfo(const QHash<ProKey, ProString> &ve if (value.contains("XCODE")) { // we don't want to generate xcode projects... // qDebug() << "default mkspec is xcode, falling back to g++"; - return baseMkspecDir.appendPath(QLatin1String("macx-g++")); + return baseMkspecDir.appendPath("macx-g++"); } } break; @@ -1612,14 +1611,14 @@ FileName BaseQtVersion::sourcePath(const QHash<ProKey, ProString> &versionInfo) const QString installData = qmakeProperty(versionInfo, "QT_INSTALL_PREFIX"); QString sourcePath = installData; - QFile qmakeCache(installData + QLatin1String("/.qmake.cache")); + QFile qmakeCache(installData + "/.qmake.cache"); if (qmakeCache.exists() && qmakeCache.open(QIODevice::ReadOnly | QIODevice::Text)) { QTextStream stream(&qmakeCache); while (!stream.atEnd()) { QString line = stream.readLine().trimmed(); - if (line.startsWith(QLatin1String("QT_SOURCE_TREE"))) { - sourcePath = line.split(QLatin1Char('=')).at(1).trimmed(); - if (sourcePath.startsWith(QLatin1String("$$quote("))) { + if (line.startsWith("QT_SOURCE_TREE")) { + sourcePath = line.split('=').at(1).trimmed(); + if (sourcePath.startsWith("$$quote(")) { sourcePath.remove(0, 8); sourcePath.chop(1); } @@ -1636,7 +1635,7 @@ bool BaseQtVersion::isInSourceDirectory(const Utils::FileName &filePath) if (source.isEmpty()) return false; QDir dir = QDir(source.toString()); - if (dir.dirName() == QLatin1String("qtbase")) + if (dir.dirName() == "qtbase") dir.cdUp(); return filePath.isChildOf(dir); } @@ -1646,7 +1645,7 @@ bool BaseQtVersion::isSubProject(const Utils::FileName &filePath) const const Utils::FileName &source = sourcePath(); if (!source.isEmpty()) { QDir dir = QDir(source.toString()); - if (dir.dirName() == QLatin1String("qtbase")) + if (dir.dirName() == "qtbase") dir.cdUp(); if (filePath.isChildOf(dir)) @@ -1720,7 +1719,7 @@ bool BaseQtVersion::isQtQuickCompilerSupported(QString *reason) const } const QString qtQuickCompilerExecutable = - HostOsInfo::withExecutableSuffix(binPath().toString() + QLatin1String("/qtquickcompiler")); + HostOsInfo::withExecutableSuffix(binPath().toString() + "/qtquickcompiler"); if (!QFileInfo::exists(qtQuickCompilerExecutable)) { if (reason) *reason = QCoreApplication::translate("BaseQtVersion", "This Qt Version does not contain Qt Quick Compiler."); @@ -1748,22 +1747,22 @@ FileNameList BaseQtVersion::qtCorePaths() const foreach (const QFileInfo &info, infoList) { const QString file = info.fileName(); if (info.isDir() - && file.startsWith(QLatin1String("QtCore")) - && file.endsWith(QLatin1String(".framework"))) { + && file.startsWith("QtCore") + && file.endsWith(".framework")) { // handle Framework FileName lib(info); - dynamicLibs.append(lib.appendPath(file.left(file.lastIndexOf(QLatin1Char('.'))))); + dynamicLibs.append(lib.appendPath(file.left(file.lastIndexOf('.')))); } else if (info.isReadable()) { - if (file.startsWith(QLatin1String("libQtCore")) - || file.startsWith(QLatin1String("libQt5Core")) - || file.startsWith(QLatin1String("QtCore")) - || file.startsWith(QLatin1String("Qt5Core"))) { - if (file.endsWith(QLatin1String(".a")) || file.endsWith(QLatin1String(".lib"))) + if (file.startsWith("libQtCore") + || file.startsWith("libQt5Core") + || file.startsWith("QtCore") + || file.startsWith("Qt5Core")) { + if (file.endsWith(".a") || file.endsWith(".lib")) staticLibs.append(FileName(info)); - else if (file.endsWith(QLatin1String(".dll")) + else if (file.endsWith(".dll") || file.endsWith(QString::fromLatin1(".so.") + versionString) - || file.endsWith(QLatin1String(".so")) - || file.endsWith(QLatin1Char('.') + versionString + QLatin1String(".dylib"))) + || file.endsWith(".so") + || file.endsWith(QLatin1Char('.') + versionString + ".dylib")) dynamicLibs.append(FileName(info)); } } 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 16e5133c561..dc8edee24b4 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -42,6 +42,7 @@ #include <sourcelocationscontainer.h> #include <tokeninfos.h> #include <filepathview.h> +#include <tooltipinfo.h> #include <cpptools/usages.h> @@ -290,6 +291,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 << "()"; @@ -406,7 +418,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 << ", " @@ -573,6 +586,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 072719ff6d1..73848ba8541 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -79,6 +79,7 @@ class CodeCompletedMessage; class EchoMessage; class DocumentAnnotationsChangedMessage; class ReferencesMessage; +class ToolTipMessage; class FollowSymbolMessage; class CompleteCodeMessage; class EndMessage; @@ -103,6 +104,7 @@ class RemovePchProjectPartsMessage; class RequestDocumentAnnotationsMessage; class RequestFollowSymbolMessage; class RequestReferencesMessage; +class RequestToolTipMessage; class RequestSourceLocationsForRenamingMessage; class RequestSourceRangesAndDiagnosticsForQueryMessage; class RequestSourceRangesForQueryMessage; @@ -124,6 +126,7 @@ template <char WindowsSlash> class AbstractFilePathView; using FilePathView = AbstractFilePathView<'/'>; using NativeFilePathView = AbstractFilePathView<'\\'>; +class ToolTipInfo; std::ostream &operator<<(std::ostream &out, const SourceLocationEntry &entry); std::ostream &operator<<(std::ostream &out, const IdPaths &idPaths); @@ -136,6 +139,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); @@ -162,6 +166,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 3c9642a46a7..257a83370c9 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 |