diff options
Diffstat (limited to 'src')
91 files changed, 1234 insertions, 494 deletions
diff --git a/src/libs/clangsupport/clangpathwatcher.h b/src/libs/clangsupport/clangpathwatcher.h index dab52996c2..7a7d6ccfcf 100644 --- a/src/libs/clangsupport/clangpathwatcher.h +++ b/src/libs/clangsupport/clangpathwatcher.h @@ -1,4 +1,4 @@ -/**************************************************************************** +; /**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ @@ -27,71 +27,94 @@ #include "clangpathwatcherinterface.h" #include "clangpathwatchernotifier.h" -#include "changedfilepathcompressor.h" +#include "directorypathcompressor.h" #include "filepathcachinginterface.h" +#include "filesystem.h" #include "stringcache.h" +#include <utils/algorithm.h> + #include <QTimer> namespace ClangBackEnd { +template<class InputIt1, class InputIt2, class Callable> +void set_greedy_intersection_call( + InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable) +{ + while (first1 != last1 && first2 != last2) { + if (*first1 < *first2) { + ++first1; + } else { + if (*first2 < *first1) + ++first2; + else + callable(*first1++); + } + } +} + class WatcherEntry { public: ProjectPartId id; - FilePathId pathId; + DirectoryPathId directoryPathId; + FilePathId filePathId; + long long lastModified = -1; friend bool operator==(WatcherEntry first, WatcherEntry second) { - return first.id == second.id && first.pathId == second.pathId; + return first.id == second.id && first.directoryPathId == second.directoryPathId + && first.filePathId == second.filePathId; } friend bool operator<(WatcherEntry first, WatcherEntry second) { - return std::tie(first.pathId, first.id) < std::tie(second.pathId, second.id); + return std::tie(first.directoryPathId, first.filePathId, first.id) + < std::tie(second.directoryPathId, second.filePathId, second.id); } - friend bool operator<(WatcherEntry entry, FilePathId pathId) + friend bool operator<(DirectoryPathId directoryPathId, WatcherEntry entry) { - return entry.pathId < pathId; + return directoryPathId < entry.directoryPathId; } - friend bool operator<(FilePathId pathId, WatcherEntry entry) + friend bool operator<(WatcherEntry entry, DirectoryPathId directoryPathId) { - return pathId < entry.pathId; + return entry.directoryPathId < directoryPathId; } - operator FilePathId() const - { - return pathId; - } + operator FilePathId() const { return filePathId; } + + operator DirectoryPathId() const { return directoryPathId; } }; using WatcherEntries = std::vector<WatcherEntry>; -template <typename FileSystemWatcher, - typename Timer> +template<typename FileSystemWatcher, typename Timer> class CLANGSUPPORT_GCCEXPORT ClangPathWatcher : public ClangPathWatcherInterface { public: ClangPathWatcher(FilePathCachingInterface &pathCache, - ClangPathWatcherNotifier *notifier=nullptr) - : m_changedFilePathCompressor(pathCache), - m_pathCache(pathCache), - m_notifier(notifier) + FileSystemInterface &fileSystem, + ClangPathWatcherNotifier *notifier = nullptr) + : m_pathCache(pathCache) + , m_fileStatusCache(fileSystem) + , m_fileSystem(fileSystem) + , m_notifier(notifier) { QObject::connect(&m_fileSystemWatcher, - &FileSystemWatcher::fileChanged, - [&] (const QString &filePath) { compressChangedFilePath(filePath); }); + &FileSystemWatcher::directoryChanged, + [&](const QString &path) { compressChangedDirectoryPath(path); }); - m_changedFilePathCompressor.setCallback([&] (ClangBackEnd::FilePathIds &&filePathIds) { - addChangedPathForFilePath(std::move(filePathIds)); + m_directoryPathCompressor.setCallback([&](ClangBackEnd::DirectoryPathIds &&directoryPathIds) { + addChangedPathForFilePath(std::move(directoryPathIds)); }); } ~ClangPathWatcher() { - m_changedFilePathCompressor.setCallback([&] (FilePathIds &&) {}); + m_directoryPathCompressor.setCallback([&](DirectoryPathIds &&) {}); } void updateIdPaths(const std::vector<IdPaths> &idPaths) override @@ -109,7 +132,7 @@ public: auto filteredPaths = filterNotWatchedPaths(removedEntries); if (!filteredPaths.empty()) - m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths)); + m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths)); } void setNotifier(ClangPathWatcherNotifier *notifier) override @@ -164,7 +187,13 @@ public: outputIterator = std::transform(idPath.filePathIds.begin(), idPath.filePathIds.end(), outputIterator, - [&] (FilePathId pathId) { return WatcherEntry{id, pathId}; }); + [&](FilePathId filePathId) { + return WatcherEntry{ + id, + m_pathCache.directoryPathId(filePathId), + filePathId, + m_fileStatusCache.lastModifiedTime(filePathId)}; + }); } std::sort(entries.begin(), entries.end()); @@ -182,7 +211,7 @@ public: mergeToWatchedEntries(newEntries); if (!filteredPaths.empty()) - m_fileSystemWatcher.addPaths(convertWatcherEntriesToQStringList(filteredPaths)); + m_fileSystemWatcher.addPaths(convertWatcherEntriesToDirectoryPathList(filteredPaths)); } void removeUnusedEntries(const WatcherEntries &entries, const ProjectPartIds &ids) @@ -194,33 +223,31 @@ public: auto filteredPaths = filterNotWatchedPaths(oldEntries); if (!filteredPaths.empty()) - m_fileSystemWatcher.removePaths(convertWatcherEntriesToQStringList(filteredPaths)); + m_fileSystemWatcher.removePaths(convertWatcherEntriesToDirectoryPathList(filteredPaths)); } - FileSystemWatcher &fileSystemWatcher() + FileSystemWatcher &fileSystemWatcher() { return m_fileSystemWatcher; } + + QStringList convertWatcherEntriesToDirectoryPathList(const DirectoryPathIds &directoryPathIds) const { - return m_fileSystemWatcher; + return Utils::transform<QStringList>(directoryPathIds, [&](DirectoryPathId id) { + return QString(m_pathCache.directoryPath(id)); + }); } - QStringList convertWatcherEntriesToQStringList( - const WatcherEntries &watcherEntries) + QStringList convertWatcherEntriesToDirectoryPathList(const WatcherEntries &watcherEntries) const { - QStringList paths; - paths.reserve(int(watcherEntries.size())); + DirectoryPathIds directoryPathIds = Utils::transform<DirectoryPathIds>( + watcherEntries, [&](WatcherEntry entry) { return entry.directoryPathId; }); - std::transform(watcherEntries.begin(), - watcherEntries.end(), - std::back_inserter(paths), - [&] (WatcherEntry entry) { - return QString(m_pathCache.filePath(entry.pathId).path()); - }); + std::sort(directoryPathIds.begin(), directoryPathIds.end()); + directoryPathIds.erase(std::unique(directoryPathIds.begin(), directoryPathIds.end()), + directoryPathIds.end()); - return paths; + return convertWatcherEntriesToDirectoryPathList(directoryPathIds); } - template <typename Compare> - WatcherEntries notWatchedEntries(const WatcherEntries &entries, - Compare compare) const + WatcherEntries notWatchedEntries(const WatcherEntries &entries) const { WatcherEntries notWatchedEntries; notWatchedEntries.reserve(entries.size()); @@ -229,24 +256,23 @@ public: entries.end(), m_watchedEntries.cbegin(), m_watchedEntries.cend(), - std::back_inserter(notWatchedEntries), - compare); + std::back_inserter(notWatchedEntries)); return notWatchedEntries; } - WatcherEntries notWatchedEntries(const WatcherEntries &entries) const + DirectoryPathIds notWatchedPaths(const DirectoryPathIds &ids) const { - return notWatchedEntries(entries, std::less<WatcherEntry>()); - } + DirectoryPathIds notWatchedDirectoryIds; + notWatchedDirectoryIds.reserve(ids.size()); - WatcherEntries notWatchedPaths(const WatcherEntries &entries) const - { - auto compare = [] (WatcherEntry first, WatcherEntry second) { - return first.pathId < second.pathId; - }; + std::set_difference(ids.begin(), + ids.end(), + m_watchedEntries.cbegin(), + m_watchedEntries.cend(), + std::back_inserter(notWatchedDirectoryIds)); - return notWatchedEntries(entries, compare); + return notWatchedDirectoryIds; } template <typename Compare> @@ -297,25 +323,24 @@ public: m_watchedEntries = std::move(newWatchedEntries); } - static - WatcherEntries uniquePaths(const WatcherEntries &pathEntries) + static DirectoryPathIds uniquePaths(const WatcherEntries &pathEntries) { - WatcherEntries uniqueEntries; - uniqueEntries.reserve(pathEntries.size()); + DirectoryPathIds uniqueDirectoryIds; + uniqueDirectoryIds.reserve(pathEntries.size()); - auto compare = [] (WatcherEntry first, WatcherEntry second) { - return first.pathId == second.pathId; + auto compare = [](WatcherEntry first, WatcherEntry second) { + return first.directoryPathId == second.directoryPathId; }; std::unique_copy(pathEntries.begin(), pathEntries.end(), - std::back_inserter(uniqueEntries), + std::back_inserter(uniqueDirectoryIds), compare); - return uniqueEntries; + return uniqueDirectoryIds; } - WatcherEntries filterNotWatchedPaths(const WatcherEntries &entries) + DirectoryPathIds filterNotWatchedPaths(const WatcherEntries &entries) const { return notWatchedPaths(uniquePaths(entries)); } @@ -351,40 +376,48 @@ public: oldEntries.end(), std::back_inserter(newWatchedEntries)); - - m_watchedEntries = newWatchedEntries; + m_watchedEntries = std::move(newWatchedEntries); } - void compressChangedFilePath(const QString &filePath) + void compressChangedDirectoryPath(const QString &path) { - m_changedFilePathCompressor.addFilePath(filePath); + m_directoryPathCompressor.addDirectoryPathId( + m_pathCache.directoryPathId(Utils::PathString{path})); } - WatcherEntries watchedEntriesForPaths(ClangBackEnd::FilePathIds &&filePathIds) + WatcherEntries watchedEntriesForPaths(ClangBackEnd::DirectoryPathIds &&directoryPathIds) { WatcherEntries foundEntries; - foundEntries.reserve(filePathIds.size()); - - for (FilePathId pathId : filePathIds) { - auto range = std::equal_range(m_watchedEntries.begin(), m_watchedEntries.end(), pathId); - foundEntries.insert(foundEntries.end(), range.first, range.second); - } + foundEntries.reserve(m_watchedEntries.size()); + + set_greedy_intersection_call(m_watchedEntries.begin(), + m_watchedEntries.end(), + directoryPathIds.begin(), + directoryPathIds.end(), + [&](WatcherEntry &entry) { + m_fileStatusCache.update(entry.filePathId); + auto currentLastModified = m_fileStatusCache.lastModifiedTime( + entry.filePathId); + if (entry.lastModified < currentLastModified) { + foundEntries.push_back(entry); + entry.lastModified = currentLastModified; + } + }); return foundEntries; } - FilePathIds watchedPaths(const FilePathIds &filePathIds) const + FilePathIds watchedPaths(const WatcherEntries &entries) const { - FilePathIds watchedFilePathIds; - watchedFilePathIds.reserve(filePathIds.size()); + auto filePathIds = Utils::transform<FilePathIds>(entries, [](WatcherEntry entry) { + return entry.filePathId; + }); + + std::sort(filePathIds.begin(), filePathIds.end()); - std::set_intersection(m_watchedEntries.begin(), - m_watchedEntries.end(), - filePathIds.begin(), - filePathIds.end(), - std::back_inserter(watchedFilePathIds)); + filePathIds.erase(std::unique(filePathIds.begin(), filePathIds.end()), filePathIds.end()); - return watchedFilePathIds; + return filePathIds; } ProjectPartIds idsForWatcherEntries(const WatcherEntries &foundEntries) @@ -403,21 +436,20 @@ public: ProjectPartIds uniqueIds(ProjectPartIds &&ids) { std::sort(ids.begin(), ids.end()); - auto newEnd = std::unique(ids.begin(), ids.end()); - ids.erase(newEnd, ids.end()); + ids.erase(std::unique(ids.begin(), ids.end()), ids.end()); return std::move(ids); } - void addChangedPathForFilePath(FilePathIds &&filePathIds) + void addChangedPathForFilePath(DirectoryPathIds &&directoryPathIds) { if (m_notifier) { - WatcherEntries foundEntries = watchedEntriesForPaths(std::move(filePathIds)); + WatcherEntries foundEntries = watchedEntriesForPaths(std::move(directoryPathIds)); ProjectPartIds changedIds = idsForWatcherEntries(foundEntries); m_notifier->pathsWithIdsChanged(uniqueIds(std::move(changedIds))); - m_notifier->pathsChanged(watchedPaths(filePathIds)); + m_notifier->pathsChanged(watchedPaths(foundEntries)); } } @@ -428,10 +460,12 @@ public: private: WatcherEntries m_watchedEntries; - ChangedFilePathCompressor<Timer> m_changedFilePathCompressor; FileSystemWatcher m_fileSystemWatcher; + FileStatusCache m_fileStatusCache; + FileSystemInterface &m_fileSystem; FilePathCachingInterface &m_pathCache; ClangPathWatcherNotifier *m_notifier; + DirectoryPathCompressor<Timer> m_directoryPathCompressor; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri index df537f5848..dc66408e58 100644 --- a/src/libs/clangsupport/clangsupport-lib.pri +++ b/src/libs/clangsupport/clangsupport-lib.pri @@ -18,6 +18,7 @@ SOURCES += \ $$PWD/clangcodemodelserverproxy.cpp \ $$PWD/alivemessage.cpp \ $$PWD/completionsmessage.cpp \ + $$PWD/filesystem.cpp \ $$PWD/requestcompletionsmessage.cpp \ $$PWD/echomessage.cpp \ $$PWD/endmessage.cpp \ @@ -87,7 +88,8 @@ SOURCES += \ $$PWD/removegeneratedfilesmessage.cpp \ $$PWD/generatedfiles.cpp \ $$PWD/projectpartartefact.cpp \ - $$PWD/projectpartcontainer.cpp + $$PWD/projectpartcontainer.cpp \ + $$PWD/filestatuscache.cpp HEADERS += \ $$PWD/cancelmessage.h \ @@ -109,7 +111,11 @@ HEADERS += \ $$PWD/alivemessage.h \ $$PWD/clangsupportexceptions.h \ $$PWD/completionsmessage.h \ + $$PWD/directoryandfilepathid.h \ + $$PWD/directorypathid.h \ $$PWD/executeinloop.h \ + $$PWD/filesystem.h \ + $$PWD/filesysteminterface.h \ $$PWD/pchpaths.h \ $$PWD/projectpartid.h \ $$PWD/projectpartsstorage.h \ @@ -151,6 +157,7 @@ HEADERS += \ $$PWD/refactoringserverinterface.h \ $$PWD/refactoringserverproxy.h \ $$PWD/referencesmessage.h \ + $$PWD/set_algorithm.h \ $$PWD/unsavedfilesupdatedmessage.h \ $$PWD/removeprojectpartsmessage.h \ $$PWD/requestannotationsmessage.h \ @@ -217,6 +224,8 @@ HEADERS += \ $$PWD/sourceentry.h \ $$PWD/modifiedtimecheckerinterface.h \ $$PWD/environment.h \ + $$PWD/filestatus.h \ + $$PWD/filestatuscache.h \ $$PWD/modifiedtimechecker.h contains(QT_CONFIG, reduce_exports):CONFIG += hide_symbols diff --git a/src/libs/clangsupport/directoryandfilepathid.h b/src/libs/clangsupport/directoryandfilepathid.h new file mode 100644 index 0000000000..8969372919 --- /dev/null +++ b/src/libs/clangsupport/directoryandfilepathid.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "directorypathid.h" +#include "filepathid.h" + +#include <QDataStream> + +#include <vector> + +namespace ClangBackEnd { +class DirectoryAndFilePathId +{ +public: + constexpr DirectoryAndFilePathId() = default; + + DirectoryAndFilePathId(const char *) = delete; + + DirectoryAndFilePathId(int directoryPathId, int filePathId) + : directoryPathId(directoryPathId) + , filePathId(filePathId) + {} + + bool isValid() const { return directoryPathId.isValid() && filePathId.isValid(); } + + friend bool operator==(DirectoryAndFilePathId first, DirectoryAndFilePathId second) + { + return first.isValid() && first.directoryPathId == second.directoryPathId + && first.filePathId == second.filePathId; + } + + friend bool operator!=(DirectoryAndFilePathId first, DirectoryAndFilePathId second) + { + return !(first == second); + } + + friend bool operator<(DirectoryAndFilePathId first, DirectoryAndFilePathId second) + { + return std::tie(first.directoryPathId, first.filePathId) + < std::tie(second.directoryPathId, second.filePathId); + } + + friend QDataStream &operator<<(QDataStream &out, + const DirectoryAndFilePathId &directoryAndFilePathId) + { + out << directoryAndFilePathId.directoryPathId; + out << directoryAndFilePathId.filePathId; + + return out; + } + + friend QDataStream &operator>>(QDataStream &in, DirectoryAndFilePathId &directoryAndFilePathId) + { + in >> directoryAndFilePathId.directoryPathId; + in >> directoryAndFilePathId.filePathId; + return in; + } + +public: + DirectoryPathId directoryPathId; + FilePathId filePathId; +}; + +using DirectoryAndFilePathIds = std::vector<DirectoryAndFilePathId>; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/changedfilepathcompressor.h b/src/libs/clangsupport/directorypathcompressor.h index aa8e1ec71a..cd02948e83 100644 --- a/src/libs/clangsupport/changedfilepathcompressor.h +++ b/src/libs/clangsupport/directorypathcompressor.h @@ -27,53 +27,44 @@ #include "clangsupport_global.h" -#include <filepathid.h> -#include <filepathcache.h> +#include "filepathcachinginterface.h" +#include <QDir> #include <QTimer> -#include <filepathcachinginterface.h> +#include <utils/algorithm.h> #include <functional> namespace ClangBackEnd { -template <typename Timer> -class ChangedFilePathCompressor +template<typename Timer> +class DirectoryPathCompressor { public: - ChangedFilePathCompressor(FilePathCachingInterface &filePathCache) - : m_filePathCache(filePathCache) - { - m_timer.setSingleShot(true); - } + DirectoryPathCompressor() { m_timer.setSingleShot(true); } - virtual ~ChangedFilePathCompressor() - { - } + virtual ~DirectoryPathCompressor() = default; - void addFilePath(const QString &filePath) + void addDirectoryPathId(DirectoryPathId directoryPathIdId) { - FilePathId filePathId = m_filePathCache.filePathId(FilePath(filePath)); + auto found = std::lower_bound(m_directoryPathIds.begin(), + m_directoryPathIds.end(), + directoryPathIdId); - auto found = std::lower_bound(m_filePaths.begin(), m_filePaths.end(), filePathId); - - if (found == m_filePaths.end() || *found != filePathId) - m_filePaths.insert(found, filePathId); + if (found == m_directoryPathIds.end() || *found != directoryPathIdId) + m_directoryPathIds.insert(found, directoryPathIdId); restartTimer(); } - FilePathIds takeFilePathIds() - { - return std::move(m_filePaths); - } + DirectoryPathIds takeDirectoryPathIds() { return std::move(m_directoryPathIds); } - virtual void setCallback(std::function<void(ClangBackEnd::FilePathIds &&)> &&callback) + virtual void setCallback(std::function<void(ClangBackEnd::DirectoryPathIds &&)> &&callback) { - QObject::connect(&m_timer, - &Timer::timeout, - [this, callback=std::move(callback)] { callback(takeFilePathIds()); }); + QObject::connect(&m_timer, &Timer::timeout, [this, callback = std::move(callback)] { + callback(takeDirectoryPathIds()); + }); } unittest_public: @@ -88,9 +79,8 @@ unittest_public: } private: - FilePathIds m_filePaths; + DirectoryPathIds m_directoryPathIds; Timer m_timer; - FilePathCachingInterface &m_filePathCache; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/directorypathid.h b/src/libs/clangsupport/directorypathid.h new file mode 100644 index 0000000000..2fd0b5847e --- /dev/null +++ b/src/libs/clangsupport/directorypathid.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 <QDataStream> + +#include <vector> + +namespace ClangBackEnd { +class DirectoryPathId +{ +public: + constexpr DirectoryPathId() = default; + + DirectoryPathId(const char *) = delete; + + DirectoryPathId(int directoryPathId) + : directoryPathId(directoryPathId) + {} + + bool isValid() const { return directoryPathId >= 0; } + + friend bool operator==(DirectoryPathId first, DirectoryPathId second) + { + return first.isValid() && first.directoryPathId == second.directoryPathId; + } + + friend bool operator!=(DirectoryPathId first, DirectoryPathId second) + { + return !(first == second); + } + + friend bool operator<(DirectoryPathId first, DirectoryPathId second) + { + return first.directoryPathId < second.directoryPathId; + } + + friend QDataStream &operator<<(QDataStream &out, const DirectoryPathId &directoryPathId) + { + out << directoryPathId.directoryPathId; + + return out; + } + + friend QDataStream &operator>>(QDataStream &in, DirectoryPathId &directoryPathId) + { + in >> directoryPathId.directoryPathId; + + return in; + } + +public: + int directoryPathId = -1; +}; + +using DirectoryPathIds = std::vector<DirectoryPathId>; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filepathcache.h b/src/libs/clangsupport/filepathcache.h index bdd925a79c..4845416d22 100644 --- a/src/libs/clangsupport/filepathcache.h +++ b/src/libs/clangsupport/filepathcache.h @@ -25,9 +25,10 @@ #pragma once +#include "directorypathid.h" +#include "filepath.h" #include "filepathexceptions.h" #include "filepathid.h" -#include "filepath.h" #include "filepathview.h" #include "stringcache.h" @@ -121,10 +122,10 @@ public: { Utils::SmallStringView directoryPath = filePath.directory(); - int directoryId = m_directoryPathCache.stringId(directoryPath, - [&] (const Utils::SmallStringView) { - return m_filePathStorage.fetchDirectoryId(directoryPath); - }); + int directoryId = m_directoryPathCache.stringId( + directoryPath, [&](const Utils::SmallStringView directoryPath) { + return m_filePathStorage.fetchDirectoryId(directoryPath); + }); Utils::SmallStringView fileName = filePath.name(); @@ -136,6 +137,17 @@ public: return fileNameId; } + DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const + { + Utils::SmallStringView path = directoryPath.back() == '/' + ? directoryPath.mid(0, directoryPath.size() - 1) + : directoryPath; + + return m_directoryPathCache.stringId(path, [&](const Utils::SmallStringView directoryPath) { + return m_filePathStorage.fetchDirectoryId(directoryPath); + }); + } + FilePath filePath(FilePathId filePathId) const { if (Q_UNLIKELY(!filePathId.isValid())) @@ -157,6 +169,32 @@ public: return FilePath{directoryPath, entry.fileName}; } + Utils::PathString directoryPath(DirectoryPathId directoryPathId) const + { + if (Q_UNLIKELY(!directoryPathId.isValid())) + throw NoDirectoryPathForInvalidDirectoryPathId(); + + auto fetchDirectoryPath = [&](int id) { return m_filePathStorage.fetchDirectoryPath(id); }; + + return m_directoryPathCache.string(directoryPathId.directoryPathId, fetchDirectoryPath); + } + + DirectoryPathId directoryPathId(FilePathId filePathId) const + { + if (Q_UNLIKELY(!filePathId.isValid())) + throw NoFilePathForInvalidFilePathId(); + + auto fetchSoureNameAndDirectoryId = [&](int id) { + auto entry = m_filePathStorage.fetchSourceNameAndDirectoryId(id); + return FileNameEntry{entry.sourceName, entry.directoryId}; + }; + + FileNameEntry entry = m_fileNameCache.string(filePathId.filePathId, + fetchSoureNameAndDirectoryId); + + return m_fileNameCache.string(filePathId.filePathId, fetchSoureNameAndDirectoryId).directoryId; + } + private: mutable DirectoryPathCache m_directoryPathCache; mutable FileNameCache m_fileNameCache; diff --git a/src/libs/clangsupport/filepathcaching.cpp b/src/libs/clangsupport/filepathcaching.cpp index 0a3fc6ee41..372ed86bd3 100644 --- a/src/libs/clangsupport/filepathcaching.cpp +++ b/src/libs/clangsupport/filepathcaching.cpp @@ -37,4 +37,19 @@ FilePath FilePathCaching::filePath(FilePathId filePathId) const return m_cache.filePath(filePathId); } +DirectoryPathId FilePathCaching::directoryPathId(Utils::SmallStringView directoryPath) const +{ + return m_cache.directoryPathId(directoryPath); +} + +Utils::PathString FilePathCaching::directoryPath(DirectoryPathId directoryPathId) const +{ + return m_cache.directoryPath(directoryPathId); +} + +DirectoryPathId FilePathCaching::directoryPathId(FilePathId filePathId) const +{ + return m_cache.directoryPathId(filePathId); +} + } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filepathcaching.h b/src/libs/clangsupport/filepathcaching.h index a3337755fd..f69b940b15 100644 --- a/src/libs/clangsupport/filepathcaching.h +++ b/src/libs/clangsupport/filepathcaching.h @@ -50,6 +50,9 @@ public: FilePathId filePathId(FilePathView filePath) const override; FilePath filePath(FilePathId filePathId) const override; + DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const override; + Utils::PathString directoryPath(DirectoryPathId directoryPathId) const override; + DirectoryPathId directoryPathId(FilePathId filePathId) const override; private: Factory m_factory; diff --git a/src/libs/clangsupport/filepathcachinginterface.h b/src/libs/clangsupport/filepathcachinginterface.h index b9668362f7..098fd39ca9 100644 --- a/src/libs/clangsupport/filepathcachinginterface.h +++ b/src/libs/clangsupport/filepathcachinginterface.h @@ -25,6 +25,7 @@ #pragma once +#include "directorypathid.h" #include "filepath.h" #include "filepathid.h" #include "filepathview.h" @@ -40,8 +41,11 @@ public: virtual FilePathId filePathId(FilePathView filePath) const = 0; virtual FilePath filePath(FilePathId filePathId) const = 0; + virtual DirectoryPathId directoryPathId(Utils::SmallStringView directoryPath) const = 0; + virtual DirectoryPathId directoryPathId(FilePathId filePathId) const = 0; + virtual Utils::PathString directoryPath(DirectoryPathId directoryPathId) const = 0; - template <typename Container> + template<typename Container> FilePathIds filePathIds(Container &&filePaths) const { FilePathIds filePathIds; diff --git a/src/libs/clangsupport/filepathexceptions.h b/src/libs/clangsupport/filepathexceptions.h index 80bded0c66..3174d89479 100644 --- a/src/libs/clangsupport/filepathexceptions.h +++ b/src/libs/clangsupport/filepathexceptions.h @@ -38,6 +38,15 @@ public: } }; +class NoDirectoryPathForInvalidDirectoryPathId : std::exception +{ +public: + const char *what() const noexcept override + { + return "You cannot get a directory path for an invalid directory path id!"; + } +}; + class SourceNameIdDoesNotExists : std::exception { public: diff --git a/src/libs/clangsupport/filepathstorage.h b/src/libs/clangsupport/filepathstorage.h index 5917504dbf..8aba2ef6d2 100644 --- a/src/libs/clangsupport/filepathstorage.h +++ b/src/libs/clangsupport/filepathstorage.h @@ -179,12 +179,32 @@ public: transaction.commit(); - return optionalSourceName.value(); + return *optionalSourceName; } catch (const Sqlite::StatementIsBusy &) { return fetchSourceNameAndDirectoryId(sourceId); } } + int fetchDirectoryId(int sourceId) + { + try { + Sqlite::DeferredTransaction transaction{m_statementFactory.database}; + + ReadStatement &statement = m_statementFactory.selectDirectoryIdFromSourcesBySourceId; + + auto optionalDirectoryId = statement.template value<int>(sourceId); + + if (!optionalDirectoryId) + throw SourceNameIdDoesNotExists(); + + transaction.commit(); + + return *optionalDirectoryId; + } catch (const Sqlite::StatementIsBusy &) { + return fetchDirectoryId(sourceId); + } + } + std::vector<Sources::Source> fetchAllSources() { try { diff --git a/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h b/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h index dbf8071775..27296a1df6 100644 --- a/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h +++ b/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h @@ -69,6 +69,8 @@ public: "SELECT sourceName, directoryId FROM sources WHERE sourceId = ?", database }; + ReadStatement selectDirectoryIdFromSourcesBySourceId{ + "SELECT directoryId FROM sources WHERE sourceId = ?", database}; WriteStatement insertIntoSources{ "INSERT INTO sources(directoryId, sourceName) VALUES (?,?)", database diff --git a/src/tools/clangrefactoringbackend/source/filestatus.h b/src/libs/clangsupport/filestatus.h index da2be4a304..da2be4a304 100644 --- a/src/tools/clangrefactoringbackend/source/filestatus.h +++ b/src/libs/clangsupport/filestatus.h diff --git a/src/libs/clangsupport/filestatuscache.cpp b/src/libs/clangsupport/filestatuscache.cpp new file mode 100644 index 0000000000..e8ed50a833 --- /dev/null +++ b/src/libs/clangsupport/filestatuscache.cpp @@ -0,0 +1,137 @@ +/**************************************************************************** +** +** Copyright (C) 2017 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 "filestatuscache.h" +#include "filesystem.h" + +#include <set_algorithm.h> + +#include <utils/algorithm.h> + +#include <QDateTime> +#include <QFileInfo> + +namespace ClangBackEnd { + +long long FileStatusCache::lastModifiedTime(FilePathId filePathId) const +{ + return findEntry(filePathId).lastModified; +} + +void FileStatusCache::update(FilePathId filePathId) +{ + auto found = std::lower_bound(m_cacheEntries.begin(), + m_cacheEntries.end(), + Internal::FileStatusCacheEntry{filePathId}, + [] (const auto &first, const auto &second) { + return first.filePathId < second.filePathId; + }); + + if (found != m_cacheEntries.end() && found->filePathId == filePathId) + found->lastModified = m_fileSystem.lastModified(filePathId); +} + +void FileStatusCache::update(FilePathIds filePathIds) +{ + std::set_intersection(m_cacheEntries.begin(), + m_cacheEntries.end(), + filePathIds.begin(), + filePathIds.end(), + make_iterator([&](auto &entry) { + entry.lastModified = m_fileSystem.lastModified(entry.filePathId); + })); +} + +FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const +{ + FilePathIds modifiedFilePathIds; + modifiedFilePathIds.reserve(filePathIds.size()); + + std::set_intersection(m_cacheEntries.begin(), + m_cacheEntries.end(), + filePathIds.begin(), + filePathIds.end(), + make_iterator([&](auto &entry) { + auto newLastModified = m_fileSystem.lastModified(entry.filePathId); + if (newLastModified > entry.lastModified) { + modifiedFilePathIds.push_back(entry.filePathId); + entry.lastModified = newLastModified; + } + })); + + Internal::FileStatusCacheEntries newEntries; + newEntries.reserve(filePathIds.size()); + + std::set_difference(filePathIds.begin(), + filePathIds.end(), + m_cacheEntries.begin(), + m_cacheEntries.end(), + make_iterator([&](FilePathId newFilePathId) { + newEntries.emplace_back(newFilePathId, + m_fileSystem.lastModified(newFilePathId)); + modifiedFilePathIds.push_back(newFilePathId); + })); + + if (newEntries.size()) { + Internal::FileStatusCacheEntries mergedEntries; + mergedEntries.reserve(m_cacheEntries.size() + newEntries.size()); + + std::set_union(newEntries.begin(), + newEntries.end(), + m_cacheEntries.begin(), + m_cacheEntries.end(), + std::back_inserter(mergedEntries)); + + m_cacheEntries = std::move(mergedEntries); + } + + std::sort(modifiedFilePathIds.begin(), modifiedFilePathIds.end()); + + return modifiedFilePathIds; +} + +FileStatusCache::size_type FileStatusCache::size() const +{ + return m_cacheEntries.size(); +} + +Internal::FileStatusCacheEntry FileStatusCache::findEntry(FilePathId filePathId) const +{ + auto found = std::lower_bound(m_cacheEntries.begin(), + m_cacheEntries.end(), + Internal::FileStatusCacheEntry{filePathId}, + [] (const auto &first, const auto &second) { + return first.filePathId < second.filePathId; + }); + + if (found != m_cacheEntries.end() && found->filePathId == filePathId) + return *found; + + auto inserted = m_cacheEntries.emplace(found, filePathId, m_fileSystem.lastModified(filePathId)); + + return *inserted; +} + +} // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/filestatuscache.h b/src/libs/clangsupport/filestatuscache.h index 23195da605..3f0af56b3a 100644 --- a/src/tools/clangrefactoringbackend/source/filestatuscache.h +++ b/src/libs/clangsupport/filestatuscache.h @@ -31,6 +31,8 @@ QT_FORWARD_DECLARE_CLASS(QFileInfo) namespace ClangBackEnd { +class FileSystemInterface; + namespace Internal { class FileStatusCacheEntry { @@ -41,8 +43,23 @@ public: lastModified(lastModified) {} + friend bool operator<(FileStatusCacheEntry first, FileStatusCacheEntry second) + { + return first.filePathId < second.filePathId; + } + + friend bool operator<(FileStatusCacheEntry first, FilePathId second) + { + return first.filePathId < second; + } + + friend bool operator<(FilePathId first, FileStatusCacheEntry second) + { + return first < second.filePathId; + } + public: - ClangBackEnd::FilePathId filePathId; + FilePathId filePathId; long long lastModified; }; @@ -50,27 +67,30 @@ using FileStatusCacheEntries = std::vector<FileStatusCacheEntry>; } -class FileStatusCache +class CLANGSUPPORT_EXPORT FileStatusCache { public: using size_type = Internal::FileStatusCacheEntries::size_type; - FileStatusCache(FilePathCachingInterface &filePathCache); + FileStatusCache(FileSystemInterface &fileSystem) + : m_fileSystem(fileSystem) + {} FileStatusCache &operator=(const FileStatusCache &) = delete; FileStatusCache(const FileStatusCache &) = delete; - long long lastModifiedTime(ClangBackEnd::FilePathId filePathId) const; - void update(ClangBackEnd::FilePathId filePathId); + long long lastModifiedTime(FilePathId filePathId) const; + void update(FilePathId filePathId); + void update(FilePathIds filePathIds); + FilePathIds modified(FilePathIds filePathIds) const; size_type size() const; private: - Internal::FileStatusCacheEntry findEntry(ClangBackEnd::FilePathId filePathId) const; - QFileInfo qFileInfo(ClangBackEnd::FilePathId filePathId) const; + Internal::FileStatusCacheEntry findEntry(FilePathId filePathId) const; private: mutable Internal::FileStatusCacheEntries m_cacheEntries; - FilePathCachingInterface &m_filePathCache; + FileSystemInterface &m_fileSystem; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filesystem.cpp b/src/libs/clangsupport/filesystem.cpp new file mode 100644 index 0000000000..5b8c037f80 --- /dev/null +++ b/src/libs/clangsupport/filesystem.cpp @@ -0,0 +1,61 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "filesystem.h" +#include "filepathcachinginterface.h" + +#include <utils/algorithm.h> + +#include <QDateTime> +#include <QDir> +#include <QFileInfo> + +namespace ClangBackEnd { + +FilePathIds FileSystem::directoryEntries(const QString &directoryPath) const +{ + QDir directory{directoryPath}; + + QFileInfoList fileInfos = directory.entryInfoList(); + + FilePathIds filePathIds = Utils::transform<FilePathIds>(fileInfos, [&](const QFileInfo &fileInfo) { + return m_filePathCache.filePathId(FilePath{fileInfo.path()}); + }); + + std::sort(filePathIds.begin(), filePathIds.end()); + + return filePathIds; +} + +long long FileSystem::lastModified(FilePathId filePathId) const +{ + QFileInfo fileInfo(QString(m_filePathCache.filePath(filePathId))); + + fileInfo.refresh(); + + return fileInfo.lastModified().toMSecsSinceEpoch() / 1000; +} + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filesystem.h b/src/libs/clangsupport/filesystem.h new file mode 100644 index 0000000000..ecf332de55 --- /dev/null +++ b/src/libs/clangsupport/filesystem.h @@ -0,0 +1,48 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "filestatuscache.h" +#include "filesysteminterface.h" + +namespace ClangBackEnd { +class FilePathCachingInterface; + +class CLANGSUPPORT_EXPORT FileSystem final : public FileSystemInterface +{ +public: + FileSystem(FilePathCachingInterface &filePathCache) + : m_filePathCache(filePathCache) + {} + + FilePathIds directoryEntries(const QString &directoryPath) const override; + long long lastModified(FilePathId filePathId) const override; + +private: + FilePathCachingInterface &m_filePathCache; +}; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filesysteminterface.h b/src/libs/clangsupport/filesysteminterface.h new file mode 100644 index 0000000000..9d64d2a93a --- /dev/null +++ b/src/libs/clangsupport/filesysteminterface.h @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 "filepathid.h" + +#include <utils/smallstringview.h> + +namespace ClangBackEnd { + +class FileSystemInterface +{ +public: + virtual FilePathIds directoryEntries(const QString &directoryPath) const = 0; + virtual long long lastModified(FilePathId filePathId) const = 0; + +protected: + ~FileSystemInterface() = default; +}; +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/modifiedtimechecker.h b/src/libs/clangsupport/modifiedtimechecker.h index c9b62c4673..00e8e3ebac 100644 --- a/src/libs/clangsupport/modifiedtimechecker.h +++ b/src/libs/clangsupport/modifiedtimechecker.h @@ -25,23 +25,23 @@ #pragma once -#include "filepathcachinginterface.h" +#include "filesysteminterface.h" #include "modifiedtimecheckerinterface.h" +#include "set_algorithm.h" #include <algorithm> #include <iterator> namespace ClangBackEnd { + template<typename SourceEntries = ::ClangBackEnd::SourceEntries> class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface<SourceEntries> { using SourceEntry = typename SourceEntries::value_type; public: - using GetModifiedTime = std::function<ClangBackEnd::TimeStamp(ClangBackEnd::FilePathView filePath)>; - ModifiedTimeChecker(GetModifiedTime &getModifiedTime, FilePathCachingInterface &filePathCache) - : m_getModifiedTime(getModifiedTime) - , m_filePathCache(filePathCache) + ModifiedTimeChecker(FileSystemInterface &fileSystem) + : m_fileSystem(fileSystem) {} bool isUpToDate(const SourceEntries &sourceEntries) const @@ -51,165 +51,101 @@ public: updateCurrentSourceTimeStamps(sourceEntries); - return compareEntries(sourceEntries); + return compareEntries(sourceEntries) && notReseted(sourceEntries); } - void pathsChanged(const FilePathIds &filePathIds) + void pathsChanged(const FilePathIds &filePathIds) override { - using SourceTimeStampReferences = std::vector<std::reference_wrapper<SourceTimeStamp>>; - - SourceTimeStampReferences timeStampsToUpdate; - timeStampsToUpdate.reserve(filePathIds.size()); - std::set_intersection(m_currentSourceTimeStamps.begin(), m_currentSourceTimeStamps.end(), filePathIds.begin(), filePathIds.end(), - std::back_inserter(timeStampsToUpdate)); + make_iterator([&](SourceTimeStamp &sourceTimeStamp) { + sourceTimeStamp.timeStamp = m_fileSystem.lastModified( + sourceTimeStamp.sourceId); + })); + } + + void reset(const FilePathIds &filePathIds) + { + FilePathIds newResetFilePathIds; + newResetFilePathIds.reserve(newResetFilePathIds.size() + m_resetFilePathIds.size()); + + std::set_union(m_resetFilePathIds.begin(), + m_resetFilePathIds.end(), + filePathIds.begin(), + filePathIds.end(), + std::back_inserter(newResetFilePathIds)); - for (SourceTimeStamp &sourceTimeStamp : timeStampsToUpdate) { - sourceTimeStamp.timeStamp = m_getModifiedTime( - m_filePathCache.filePath(sourceTimeStamp.sourceId)); - } + m_resetFilePathIds = std::move(newResetFilePathIds); } private: bool compareEntries(const SourceEntries &sourceEntries) const { - class CompareSourceId - { - public: - bool operator()(SourceTimeStamp first, SourceTimeStamp second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) - { - return first.sourceId < second.sourceId; - } - }; - - SourceTimeStamps currentSourceTimeStamp; - currentSourceTimeStamp.reserve(sourceEntries.size()); - std::set_intersection(m_currentSourceTimeStamps.begin(), - m_currentSourceTimeStamps.end(), - sourceEntries.begin(), - sourceEntries.end(), - std::back_inserter(currentSourceTimeStamp), - CompareSourceId{}); - - class CompareTime - { - public: - bool operator()(SourceTimeStamp first, SourceTimeStamp second) - { - return first.timeStamp <= second.timeStamp; - } - - bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) - { - return first.timeStamp <= second.timeStamp; - } - - bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) - { - return first.timeStamp <= second.timeStamp; - } - - bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) - { - return first.timeStamp <= second.timeStamp; - } - }; - - return std::lexicographical_compare(currentSourceTimeStamp.begin(), - currentSourceTimeStamp.end(), - sourceEntries.begin(), - sourceEntries.end(), - CompareTime{}); + return set_intersection_compare( + m_currentSourceTimeStamps.begin(), + m_currentSourceTimeStamps.end(), + sourceEntries.begin(), + sourceEntries.end(), + [](auto first, auto second) { return second.timeStamp > first.timeStamp; }, + [](auto first, auto second) { return first.sourceId < second.sourceId; }); } void updateCurrentSourceTimeStamps(const SourceEntries &sourceEntries) const { SourceTimeStamps sourceTimeStamps = newSourceTimeStamps(sourceEntries); - for (SourceTimeStamp &newSourceTimeStamp : sourceTimeStamps) { - newSourceTimeStamp.timeStamp = m_getModifiedTime( - m_filePathCache.filePath(newSourceTimeStamp.sourceId)); - } - auto split = sourceTimeStamps.insert(sourceTimeStamps.end(), m_currentSourceTimeStamps.begin(), m_currentSourceTimeStamps.end()); std::inplace_merge(sourceTimeStamps.begin(), split, sourceTimeStamps.end()); - m_currentSourceTimeStamps = sourceTimeStamps; + m_currentSourceTimeStamps = std::move(sourceTimeStamps); } SourceTimeStamps newSourceTimeStamps(const SourceEntries &sourceEntries) const { - SourceEntries newSourceEntries; - newSourceEntries.reserve(sourceEntries.size()); - - class CompareSourceId - { - public: - bool operator()(SourceTimeStamp first, SourceTimeStamp second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(::ClangBackEnd::SourceEntry first, ::ClangBackEnd::SourceEntry second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(SourceTimeStamp first, ::ClangBackEnd::SourceEntry second) - { - return first.sourceId < second.sourceId; - } - - bool operator()(::ClangBackEnd::SourceEntry first, SourceTimeStamp second) - { - return first.sourceId < second.sourceId; - } - }; + SourceTimeStamps newTimeStamps; + newTimeStamps.reserve(sourceEntries.size()); std::set_difference(sourceEntries.begin(), sourceEntries.end(), m_currentSourceTimeStamps.begin(), m_currentSourceTimeStamps.end(), - std::back_inserter(newSourceEntries), - CompareSourceId{}); + make_iterator([&](const SourceEntry &sourceEntry) { + newTimeStamps.emplace_back(sourceEntry.sourceId, + m_fileSystem.lastModified( + sourceEntry.sourceId)); + }), + [](auto first, auto second) { + return first.sourceId < second.sourceId && first.timeStamp > 0; + }); - SourceTimeStamps newTimeStamps; - newTimeStamps.reserve(newSourceEntries.size()); + return newTimeStamps; + } - std::transform(newSourceEntries.begin(), - newSourceEntries.end(), - std::back_inserter(newTimeStamps), - [](SourceEntry entry) { - return SourceTimeStamp{entry.sourceId, {}}; - }); + bool notReseted(const SourceEntries &sourceEntries) const + { + auto oldSize = m_resetFilePathIds.size(); + FilePathIds newResetFilePathIds; + newResetFilePathIds.reserve(newResetFilePathIds.size()); - return newTimeStamps; + std::set_difference(m_resetFilePathIds.begin(), + m_resetFilePathIds.end(), + sourceEntries.begin(), + sourceEntries.end(), + std::back_inserter(newResetFilePathIds)); + + m_resetFilePathIds = std::move(newResetFilePathIds); + + return oldSize == m_resetFilePathIds.size(); } private: mutable SourceTimeStamps m_currentSourceTimeStamps; - GetModifiedTime &m_getModifiedTime; - FilePathCachingInterface &m_filePathCache; + mutable FilePathIds m_resetFilePathIds; + FileSystemInterface &m_fileSystem; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/modifiedtimecheckerinterface.h b/src/libs/clangsupport/modifiedtimecheckerinterface.h index a0e79b0701..b48c38869e 100644 --- a/src/libs/clangsupport/modifiedtimecheckerinterface.h +++ b/src/libs/clangsupport/modifiedtimecheckerinterface.h @@ -38,6 +38,7 @@ public: ModifiedTimeCheckerInterface &operator=(const ModifiedTimeCheckerInterface &) = delete; virtual bool isUpToDate(const SourceEntries &sourceEntries) const = 0; + virtual void pathsChanged(const FilePathIds &filePathIds) = 0; protected: ~ModifiedTimeCheckerInterface() = default; diff --git a/src/libs/clangsupport/refactoringdatabaseinitializer.h b/src/libs/clangsupport/refactoringdatabaseinitializer.h index a7058d3204..6042264a5d 100644 --- a/src/libs/clangsupport/refactoringdatabaseinitializer.h +++ b/src/libs/clangsupport/refactoringdatabaseinitializer.h @@ -144,11 +144,11 @@ public: table.setName("projectPartsFiles"); const Sqlite::Column &projectPartIdColumn = table.addColumn("projectPartId", Sqlite::ColumnType::Integer); const Sqlite::Column &sourceIdColumn = table.addColumn("sourceId", Sqlite::ColumnType::Integer); - table.addColumn("sourceType", Sqlite::ColumnType::Integer); + const Sqlite::Column &sourceType = table.addColumn("sourceType", Sqlite::ColumnType::Integer); table.addColumn("pchCreationTimeStamp", Sqlite::ColumnType::Integer); table.addColumn("hasMissingIncludes", Sqlite::ColumnType::Integer); table.addUniqueIndex({sourceIdColumn, projectPartIdColumn}); - table.addIndex({projectPartIdColumn}); + table.addIndex({projectPartIdColumn, sourceType}); table.initialize(database); } diff --git a/src/libs/clangsupport/set_algorithm.h b/src/libs/clangsupport/set_algorithm.h new file mode 100644 index 0000000000..39bd3a2055 --- /dev/null +++ b/src/libs/clangsupport/set_algorithm.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2019 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 <algorithm> + +namespace ClangBackEnd { + +template<class Callable> +class function_output_iterator +{ +public: + typedef std::output_iterator_tag iterator_category; + typedef void value_type; + typedef void difference_type; + typedef void pointer; + typedef void reference; + + explicit function_output_iterator() {} + + explicit function_output_iterator(const Callable &callable) + : m_callable(&callable) + {} + + function_output_iterator &operator=(const function_output_iterator &iterator) + { + m_callable = iterator.m_callable; + + return *this; + } + + struct helper + { + helper(const Callable *callable) + : m_callable(callable) + {} + template<class T> + helper &operator=(T &&value) + { + (*m_callable)(std::forward<T>(value)); + return *this; + } + const Callable *m_callable; + }; + + helper operator*() { return helper(m_callable); } + function_output_iterator &operator++() { return *this; } + function_output_iterator &operator++(int) { return *this; } + +private: + const Callable *m_callable; +}; + +template<typename Callable> +function_output_iterator<Callable> make_iterator(const Callable &callable) +{ + return function_output_iterator<Callable>(callable); +} + +template<class InputIt1, class InputIt2, class Callable, class Compare> +bool set_intersection_compare( + InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable call, Compare comp) +{ + while (first1 != last1 && first2 != last2) { + if (comp(*first1, *first2)) { + ++first1; + } else { + if (!comp(*first2, *first1)) { + if (call(*first2, *first1++)) + return false; + } + ++first2; + } + } + + return true; +} +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/sourceentry.h b/src/libs/clangsupport/sourceentry.h index c593f1fcd3..5e8769d14f 100644 --- a/src/libs/clangsupport/sourceentry.h +++ b/src/libs/clangsupport/sourceentry.h @@ -131,6 +131,10 @@ public: return first.sourceId < second.sourceId; } + friend bool operator<(SourceEntry first, FilePathId second) { return first.sourceId < second; } + + friend bool operator<(FilePathId first, SourceEntry second) { return first < second.sourceId; } + friend bool operator==(SourceEntry first, SourceEntry second) { return first.sourceId == second.sourceId && first.sourceType == second.sourceType diff --git a/src/libs/languageserverprotocol/icontent.h b/src/libs/languageserverprotocol/icontent.h index af14604739..1b57165b2e 100644 --- a/src/libs/languageserverprotocol/icontent.h +++ b/src/libs/languageserverprotocol/icontent.h @@ -92,7 +92,7 @@ using ResponseHandler = std::function<void(const QByteArray &, QTextCodec *)>; using ResponseHandlers = std::function<void(MessageId, const QByteArray &, QTextCodec *)>; using MethodHandler = std::function<void(const QString, MessageId, const IContent *)>; -inline LANGUAGESERVERPROTOCOL_EXPORT uint qHash(const LanguageServerProtocol::MessageId &id) +inline uint qHash(const LanguageServerProtocol::MessageId &id) { if (Utils::holds_alternative<int>(id)) return QT_PREPEND_NAMESPACE(qHash(Utils::get<int>(id))); @@ -102,8 +102,7 @@ inline LANGUAGESERVERPROTOCOL_EXPORT uint qHash(const LanguageServerProtocol::Me } template <typename Error> -inline LANGUAGESERVERPROTOCOL_EXPORT QDebug operator<<(QDebug stream, - const LanguageServerProtocol::MessageId &id) +inline QDebug operator<<(QDebug stream, const LanguageServerProtocol::MessageId &id) { if (Utils::holds_alternative<int>(id)) stream << Utils::get<int>(id); diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index deb95ea1d3..e5243830cd 100644 --- a/src/libs/qmljs/qmljscheck.cpp +++ b/src/libs/qmljs/qmljscheck.cpp @@ -1676,7 +1676,7 @@ bool Check::visit(CallExpression *ast) if (!whiteListedFunction && !isMathFunction && !isDateFunction && !isDirectInConnectionsScope) addMessage(ErrFunctionsNotSupportedInQmlUi, location); - static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"}; + static const QStringList globalFunctions = {"String", "Boolean", "Date", "Number", "Object", "Array", "QT_TR_NOOP", "QT_TRANSLATE_NOOP", "QT_TRID_NOOP"}; if (!name.isEmpty() && name.at(0).isUpper() && !globalFunctions.contains(name)) { addMessage(WarnExpectedNewWithUppercaseFunction, location); diff --git a/src/libs/utils/smallstringview.h b/src/libs/utils/smallstringview.h index 6052ff8777..911ba2eafd 100644 --- a/src/libs/utils/smallstringview.h +++ b/src/libs/utils/smallstringview.h @@ -179,6 +179,10 @@ public: return m_pointer[0] == characterToSearch; } + char back() const { return m_pointer[m_size - 1]; } + + char operator[](std::size_t index) { return m_pointer[index]; } + private: const char *m_pointer = ""; size_type m_size = 0; diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp index eeeeb599de..614a61a6c2 100644 --- a/src/plugins/autotest/autotestunittests.cpp +++ b/src/plugins/autotest/autotestunittests.cpp @@ -77,8 +77,9 @@ void AutoTestUnitTests::initTestCase() if (!qgetenv("BOOST_INCLUDE_DIR").isEmpty()) { m_checkBoost = true; } else { - if (QFileInfo::exists("/usr/include/boost/version.hpp") - || QFileInfo::exists("/usr/local/include/boost/version.hpp")) { + if (Utils::HostOsInfo::isLinuxHost() + && (QFileInfo::exists("/usr/include/boost/version.hpp") + || QFileInfo::exists("/usr/local/include/boost/version.hpp"))) { qDebug() << "Found boost at system level - will run boost parser test."; m_checkBoost = true; } diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h index e16c1fb996..91c38cb62f 100644 --- a/src/plugins/autotest/boost/boosttestoutputreader.h +++ b/src/plugins/autotest/boost/boosttestoutputreader.h @@ -37,6 +37,7 @@ enum class ReportLevel; class BoostTestOutputReader : public TestOutputReader { + Q_OBJECT public: BoostTestOutputReader(const QFutureInterface<TestResultPtr> &futureInterface, QProcess *testApplication, const QString &buildDirectory, diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index f18bcdaa8b..ad70d78b16 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -411,9 +411,9 @@ QList<ToolChain *> IarToolChainFactory::autoDetect(const QList<ToolChain *> &alr QString registryKey; QString subExePath; } knowToolchains[] = { - {"EWARM", "\\arm\\bin\\iccarm.exe"}, - {"EWAVR", "\\avr\\bin\\iccavr.exe"}, - {"EW8051", "\\8051\\bin\\icc8051.exe"}, + {{"EWARM"}, {"\\arm\\bin\\iccarm.exe"}}, + {{"EWAVR"}, {"\\avr\\bin\\iccavr.exe"}}, + {{"EW8051"}, {"\\8051\\bin\\icc8051.exe"}}, }; QSettings registry(kRegistryNode, QSettings::NativeFormat); diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index c8f7d9f7d1..403433aec6 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -144,7 +144,7 @@ void ClangCodeModelPlugin::createCompilationDBButton() message = tr("Clang compilation database generated at \"%1\".") .arg(QDir::toNativeSeparators(result.filePath)); } else { - message = tr("Generating clang compilation database failed: %1").arg(result.error); + message = tr("Generating Clang compilation database failed: %1").arg(result.error); } Core::MessageManager::write(message, Core::MessageManager::Flash); m_generateCompilationDBAction->setEnabled( diff --git a/src/plugins/clangtools/clangselectablefilesdialog.cpp b/src/plugins/clangtools/clangselectablefilesdialog.cpp index e48679aa05..c6ad76ff0a 100644 --- a/src/plugins/clangtools/clangselectablefilesdialog.cpp +++ b/src/plugins/clangtools/clangselectablefilesdialog.cpp @@ -295,6 +295,7 @@ SelectableFilesDialog::SelectableFilesDialog(const ProjectInfo &projectInfo, CppTools::ClangDiagnosticConfigsSelectionWidget *diagnosticConfigsSelectionWidget = m_ui->clangToolsBasicSettings->ui()->clangDiagnosticConfigsSelectionWidget; QCheckBox *buildBeforeAnalysis = m_ui->clangToolsBasicSettings->ui()->buildBeforeAnalysis; + buildBeforeAnalysis->setToolTip(hintAboutBuildBeforeAnalysis()); ClangToolsProjectSettings *settings = ClangToolsProjectSettingsManager::getSettings(m_project); m_customDiagnosticConfig = diagnosticConfiguration(settings); @@ -337,6 +338,8 @@ SelectableFilesDialog::SelectableFilesDialog(const ProjectInfo &projectInfo, m_customDiagnosticConfig = currentConfigId; }); connect(buildBeforeAnalysis, &QCheckBox::toggled, [this](bool checked) { + if (!checked) + showHintAboutBuildBeforeAnalysis(); if (m_ui->globalOrCustom->currentIndex() == CustomSettings) m_buildBeforeAnalysis = checked; }); diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index b1572b0010..0c2ca841e6 100644 --- a/src/plugins/clangtools/clangtoolruncontrol.cpp +++ b/src/plugins/clangtools/clangtoolruncontrol.cpp @@ -477,6 +477,16 @@ void ClangToolRunControl::finalize() if (m_filesNotAnalyzed != 0) { QString msg = tr("%1: Not all files could be analyzed.").arg(toolName); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); + if (m_target && !m_target->activeBuildConfiguration()->buildDirectory().exists() + && !ClangToolsProjectSettingsManager::getSettings(m_target->project()) + ->buildBeforeAnalysis()) { + msg = tr("%1: You might need to build the project to generate or update source " + "files. To build automatically, enable \"Build the project before starting " + "analysis\".") + .arg(toolName); + TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); + } + TaskHub::requestPopup(); } diff --git a/src/plugins/clangtools/clangtoolsconfigwidget.cpp b/src/plugins/clangtools/clangtoolsconfigwidget.cpp index ad966ab518..b634875f97 100644 --- a/src/plugins/clangtools/clangtoolsconfigwidget.cpp +++ b/src/plugins/clangtools/clangtoolsconfigwidget.cpp @@ -56,9 +56,12 @@ ClangToolsConfigWidget::ClangToolsConfigWidget( [settings](int count) { settings->setSimultaneousProcesses(count); }); QCheckBox *buildBeforeAnalysis = m_ui->clangToolsBasicSettings->ui()->buildBeforeAnalysis; + buildBeforeAnalysis->setToolTip(hintAboutBuildBeforeAnalysis()); buildBeforeAnalysis->setCheckState(settings->savedBuildBeforeAnalysis() ? Qt::Checked : Qt::Unchecked); connect(buildBeforeAnalysis, &QCheckBox::toggled, [settings](bool checked) { + if (!checked) + showHintAboutBuildBeforeAnalysis(); settings->setBuildBeforeAnalysis(checked); }); diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp index bc8a3cabb6..55573c8ee6 100644 --- a/src/plugins/clangtools/clangtoolsprojectsettings.cpp +++ b/src/plugins/clangtools/clangtoolsprojectsettings.cpp @@ -87,7 +87,9 @@ void ClangToolsProjectSettings::load() m_useGlobalSettings = useGlobalVariant.isValid() ? useGlobalVariant.toBool() : true; m_diagnosticConfig = Core::Id::fromSetting( m_project->namedSettings(SETTINGS_KEY_DIAGNOSTIC_CONFIG)); - m_buildBeforeAnalysis = m_project->namedSettings(SETTINGS_KEY_BUILD_BEFORE_ANALYSIS).toBool(); + + const QVariant value = m_project->namedSettings(SETTINGS_KEY_BUILD_BEFORE_ANALYSIS); + m_buildBeforeAnalysis = value.isValid() ? value.toBool() : true; auto toFileName = [](const QString &s) { return Utils::FilePath::fromString(s); }; diff --git a/src/plugins/clangtools/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 3e811500ac..173d97e31d 100644 --- a/src/plugins/clangtools/clangtoolsutils.cpp +++ b/src/plugins/clangtools/clangtoolsutils.cpp @@ -25,6 +25,7 @@ #include "clangtoolsutils.h" +#include "clangtool.h" #include "clangtoolsdiagnostic.h" #include "clangtoolssettings.h" @@ -32,8 +33,9 @@ #include <projectexplorer/projectexplorerconstants.h> -#include <utils/hostosinfo.h> +#include <utils/checkablemessagebox.h> #include <utils/environment.h> +#include <utils/hostosinfo.h> #include <utils/synchronousprocess.h> #include <QCoreApplication> @@ -49,5 +51,24 @@ QString createFullLocationString(const Debugger::DiagnosticLocation &location) + QLatin1Char(':') + QString::number(location.column); } +QString hintAboutBuildBeforeAnalysis() +{ + return ClangTool::tr( + "In general, the project should be built before starting the analysis to ensure that the " + "code to analyze is valid.<br/><br/>" + "Building the project might also run code generators that update the source files as " + "necessary."); +} + +void showHintAboutBuildBeforeAnalysis() +{ + Utils::CheckableMessageBox::doNotShowAgainInformation( + Core::ICore::dialogParent(), + ClangTool::tr("Info About Build the Project Before Analysis"), + hintAboutBuildBeforeAnalysis(), + Core::ICore::settings(), + "ClangToolsDisablingBuildBeforeAnalysisHint"); +} + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/clangtools/clangtoolsutils.h b/src/plugins/clangtools/clangtoolsutils.h index 6d3a7a90ca..05acb953c5 100644 --- a/src/plugins/clangtools/clangtoolsutils.h +++ b/src/plugins/clangtools/clangtoolsutils.h @@ -41,5 +41,8 @@ namespace Internal { QString createFullLocationString(const Debugger::DiagnosticLocation &location); +QString hintAboutBuildBeforeAnalysis(); +void showHintAboutBuildBeforeAnalysis(); + } // namespace Internal } // namespace ClangTools diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 813d12739d..f1073e6bb2 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.cpp +++ b/src/plugins/coreplugin/editormanager/editormanager.cpp @@ -2429,12 +2429,12 @@ void EditorManager::closeOtherDocuments(IDocument *document) closeDocuments(documentsToClose, true); } -void EditorManager::closeAllDocuments() +bool EditorManager::closeAllDocuments() { // Only close the files that aren't pinned. const QList<DocumentModel::Entry *> entriesToClose = Utils::filtered(DocumentModel::entries(), Utils::equal(&DocumentModel::Entry::pinned, false)); - EditorManager::closeDocuments(entriesToClose); + return EditorManager::closeDocuments(entriesToClose); } // SLOT connected to action @@ -2631,7 +2631,7 @@ void EditorManager::closeDocument(DocumentModel::Entry *entry) closeDocuments({entry->document}); } -void EditorManager::closeDocuments(const QList<DocumentModel::Entry *> &entries) +bool EditorManager::closeDocuments(const QList<DocumentModel::Entry *> &entries) { QList<IDocument *> documentsToClose; for (DocumentModel::Entry *entry : entries) { @@ -2642,7 +2642,7 @@ void EditorManager::closeDocuments(const QList<DocumentModel::Entry *> &entries) else documentsToClose << entry->document; } - closeDocuments(documentsToClose); + return closeDocuments(documentsToClose); } bool EditorManager::closeEditors(const QList<IEditor*> &editorsToClose, bool askAboutModifiedEditors) diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 41429aedb1..5be379ceb2 100644 --- a/src/plugins/coreplugin/editormanager/editormanager.h +++ b/src/plugins/coreplugin/editormanager/editormanager.h @@ -128,9 +128,9 @@ public: static bool closeDocument(IDocument *document, bool askAboutModifiedEditors = true); static bool closeDocuments(const QList<IDocument *> &documents, bool askAboutModifiedEditors = true); static void closeDocument(DocumentModel::Entry *entry); - static void closeDocuments(const QList<DocumentModel::Entry *> &entries); + static bool closeDocuments(const QList<DocumentModel::Entry *> &entries); static void closeOtherDocuments(IDocument *document); - static void closeAllDocuments(); + static bool closeAllDocuments(); static void addCurrentPositionToNavigationHistory(const QByteArray &saveState = QByteArray()); static void cutForwardNavigationHistory(); diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index 8fde404064..aab9845f1a 100644 --- a/src/plugins/coreplugin/idocument.cpp +++ b/src/plugins/coreplugin/idocument.cpp @@ -79,7 +79,6 @@ public: bool hasWriteWarning = false; bool restored = false; bool isSuspendAllowed = false; - bool pinned = false; }; } // namespace Internal diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h index d219e2aeff..b7800a65c8 100644 --- a/src/plugins/coreplugin/ioutputpane.h +++ b/src/plugins/coreplugin/ioutputpane.h @@ -41,6 +41,8 @@ class QWidget; QT_END_NAMESPACE namespace Core { +class CommandButton; +class IContext; class CORE_EXPORT IOutputPane : public QObject { @@ -93,6 +95,7 @@ signals: void setBadgeNumber(int number); void zoomIn(int range); void zoomOut(int range); + void resetZoom(); void wheelZoomEnabledChanged(bool enabled); void fontChanged(const QFont &font); @@ -103,7 +106,7 @@ protected: Qt::CaseSensitivity filterCaseSensitivity() const { return m_filterCaseSensitivity; } void setFilteringEnabled(bool enable); QWidget *filterWidget() const { return m_filterOutputLineEdit; } - + void setupContext(const char *context, QWidget *widget); void setZoomButtonsEnabled(bool enabled); private: @@ -115,11 +118,12 @@ private: Id filterRegexpActionId() const; Id filterCaseSensitivityActionId() const; - QToolButton * const m_zoomInButton = nullptr; - QToolButton * const m_zoomOutButton = nullptr; + Core::CommandButton * const m_zoomInButton; + Core::CommandButton * const m_zoomOutButton; QAction *m_filterActionRegexp = nullptr; QAction *m_filterActionCaseSensitive = nullptr; Utils::FancyLineEdit *m_filterOutputLineEdit = nullptr; + IContext *m_context = nullptr; bool m_filterRegexp = false; Qt::CaseSensitivity m_filterCaseSensitivity = Qt::CaseInsensitive; }; diff --git a/src/plugins/coreplugin/messageoutputwindow.cpp b/src/plugins/coreplugin/messageoutputwindow.cpp index 7aabaefbbc..c7c752e155 100644 --- a/src/plugins/coreplugin/messageoutputwindow.cpp +++ b/src/plugins/coreplugin/messageoutputwindow.cpp @@ -56,6 +56,7 @@ MessageOutputWindow::MessageOutputWindow() connect(this, &IOutputPane::zoomIn, m_widget, &Core::OutputWindow::zoomIn); connect(this, &IOutputPane::zoomOut, m_widget, &Core::OutputWindow::zoomOut); + connect(this, &IOutputPane::resetZoom, m_widget, &Core::OutputWindow::resetZoom); connect(this, &IOutputPane::fontChanged, m_widget, &OutputWindow::setBaseFont); connect(this, &IOutputPane::wheelZoomEnabledChanged, m_widget, &OutputWindow::setWheelZoomEnabled); @@ -65,6 +66,7 @@ MessageOutputWindow::MessageOutputWindow() setupFilterUi("MessageOutputPane.Filter"); setFilteringEnabled(true); + setupContext(Constants::C_GENERAL_OUTPUT_PANE, m_widget); } MessageOutputWindow::~MessageOutputWindow() diff --git a/src/plugins/coreplugin/outputpanemanager.cpp b/src/plugins/coreplugin/outputpanemanager.cpp index 951fed419b..7cf2ec2c90 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -35,6 +35,7 @@ #include <coreplugin/actionmanager/actionmanager.h> #include <coreplugin/actionmanager/actioncontainer.h> #include <coreplugin/actionmanager/command.h> +#include <coreplugin/actionmanager/commandbutton.h> #include <coreplugin/editormanager/editormanager.h> #include <coreplugin/editormanager/ieditor.h> #include <coreplugin/find/optionspopup.h> @@ -90,24 +91,27 @@ static bool g_managerConstructed = false; // For debugging reasons. IOutputPane::IOutputPane(QObject *parent) : QObject(parent), - m_zoomInButton(new QToolButton), - m_zoomOutButton(new QToolButton) + m_zoomInButton(new Core::CommandButton), + m_zoomOutButton(new Core::CommandButton) { // We need all pages first. Ignore latecomers and shout. QTC_ASSERT(!g_managerConstructed, return); g_outputPanes.append(OutputPaneData(this)); - m_zoomInButton->setToolTip(tr("Increase Font Size")); m_zoomInButton->setIcon(Utils::Icons::PLUS_TOOLBAR.icon()); + m_zoomInButton->setCommandId(Constants::ZOOM_IN); connect(m_zoomInButton, &QToolButton::clicked, this, [this] { emit zoomIn(1); }); - m_zoomOutButton->setToolTip(tr("Decrease Font Size")); m_zoomOutButton->setIcon(Utils::Icons::MINUS.icon()); + m_zoomOutButton->setCommandId(Constants::ZOOM_OUT); connect(m_zoomOutButton, &QToolButton::clicked, this, [this] { emit zoomOut(1); }); } IOutputPane::~IOutputPane() { + if (m_context) + ICore::removeContextObject(m_context); + const int i = Utils::indexOf(g_outputPanes, Utils::equal(&OutputPaneData::pane, this)); QTC_ASSERT(i >= 0, return); delete g_outputPanes.at(i).button; @@ -174,6 +178,26 @@ void IOutputPane::setFilteringEnabled(bool enable) m_filterOutputLineEdit->setEnabled(enable); } +void IOutputPane::setupContext(const char *context, QWidget *widget) +{ + QTC_ASSERT(!m_context, return); + m_context = new IContext(this); + m_context->setContext(Context(context)); + m_context->setWidget(widget); + ICore::addContextObject(m_context); + + const auto zoomInAction = new QAction(this); + Core::ActionManager::registerAction(zoomInAction, Constants::ZOOM_IN, m_context->context()); + connect(zoomInAction, &QAction::triggered, this, [this] { emit zoomIn(1); }); + const auto zoomOutAction = new QAction(this); + Core::ActionManager::registerAction(zoomOutAction, Constants::ZOOM_OUT, m_context->context()); + connect(zoomOutAction, &QAction::triggered, this, [this] { emit zoomOut(1); }); + const auto resetZoomAction = new QAction(this); + Core::ActionManager::registerAction(resetZoomAction, Constants::ZOOM_RESET, + m_context->context()); + connect(resetZoomAction, &QAction::triggered, this, &IOutputPane::resetZoom); +} + void IOutputPane::setZoomButtonsEnabled(bool enabled) { m_zoomInButton->setEnabled(enabled); diff --git a/src/plugins/coreplugin/outputwindow.h b/src/plugins/coreplugin/outputwindow.h index 88692d5ab7..204a1106a9 100644 --- a/src/plugins/coreplugin/outputwindow.h +++ b/src/plugins/coreplugin/outputwindow.h @@ -75,6 +75,7 @@ public: void setBaseFont(const QFont &newFont); float fontZoom() const; void setFontZoom(float zoom); + void resetZoom() { setFontZoom(0); } void setWheelZoomEnabled(bool enabled); void updateFilterProperties(const QString &filterText, Qt::CaseSensitivity caseSensitivity, bool regexp); diff --git a/src/plugins/cppeditor/cppparsecontext.cpp b/src/plugins/cppeditor/cppparsecontext.cpp index 7c98fbba8f..521cc49328 100644 --- a/src/plugins/cppeditor/cppparsecontext.cpp +++ b/src/plugins/cppeditor/cppparsecontext.cpp @@ -111,14 +111,16 @@ QString ParseContextModel::currentId() const return m_projectParts[m_currentIndex]->id(); } -int ParseContextModel::rowCount(const QModelIndex &) const +int ParseContextModel::rowCount(const QModelIndex &parent) const { + if (parent.isValid()) + return 0; return m_projectParts.size(); } QVariant ParseContextModel::data(const QModelIndex &index, int role) const { - if (m_projectParts.isEmpty()) + if (!index.isValid() || index.row() < 0 || index.row() >= m_projectParts.size()) return QVariant(); const int row = index.row(); diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 7d12b33f05..3d74ad8d32 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -709,6 +709,7 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() qgetenv("QTC_CLANG_CMD_OPTIONS_BLACKLIST")) .split(';', QString::SkipEmptyParts); + const Core::Id &toolChain = m_projectPart.toolchainType; bool containsDriverMode = false; bool skipNext = false; for (const QString &option : m_projectPart.compilerFlags) { @@ -720,6 +721,13 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() if (userBlackList.contains(option)) continue; + // TODO: Make it possible that the clang binary/driver ignores unknown options, + // as it is done for libclang/clangd (not checking for OPT_UNKNOWN). + if (toolChain == ProjectExplorer::Constants::MINGW_TOOLCHAIN_TYPEID) { + if (option == "-fkeep-inline-dllexport" || option == "-fno-keep-inline-dllexport") + continue; + } + // Ignore warning flags as these interfere with our user-configured diagnostics. // Note that once "-w" is provided, no warnings will be emitted, even if "-Wall" follows. if (m_useBuildSystemWarnings == UseBuildSystemWarnings::No @@ -755,7 +763,7 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() // Check whether a language version is already used. QString theOption = option; - if (theOption.startsWith("-std=")) { + if (theOption.startsWith("-std=") || theOption.startsWith("--std=")) { m_compilerFlags.isLanguageVersionSpecified = true; theOption.replace("=c18", "=c17"); theOption.replace("=gnu18", "=gnu17"); @@ -772,7 +780,6 @@ void CompilerOptionsBuilder::evaluateCompilerFlags() m_compilerFlags.flags.append(theOption); } - const Core::Id &toolChain = m_projectPart.toolchainType; if (!containsDriverMode && (toolChain == ProjectExplorer::Constants::MSVC_TOOLCHAIN_TYPEID || toolChain == ProjectExplorer::Constants::CLANG_CL_TOOLCHAIN_TYPEID)) { diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 26c075dfe1..79f779a295 100644 --- a/src/plugins/debugger/debuggerplugin.cpp +++ b/src/plugins/debugger/debuggerplugin.cpp @@ -1759,9 +1759,9 @@ void DebuggerPlugin::attachExternalApplication(RunControl *rc) ProcessHandle pid = rc->applicationProcessHandle(); auto runControl = new RunControl(ProjectExplorer::Constants::DEBUG_RUN_MODE); runControl->setTarget(rc->target()); + runControl->setDisplayName(tr("Process %1").arg(pid.pid())); auto debugger = new DebuggerRunTool(runControl); debugger->setAttachPid(pid); - debugger->setRunControlName(tr("Process %1").arg(pid.pid())); debugger->setStartMode(AttachExternal); debugger->setCloseMode(DetachAtClose); debugger->startRunControl(); diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 50465770d9..a1b8db2169 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -605,7 +605,7 @@ BaseSettingsWidget::BaseSettingsWidget(const BaseSettings *settings, QWidget *pa mainLayout->addLayout(mimeLayout, row, 1); m_filePattern->setPlaceholderText(tr("File pattern")); mainLayout->addWidget(m_filePattern, ++row, 1); - mainLayout->addWidget(new QLabel(tr("Startup Behavior:")), ++row, 0); + mainLayout->addWidget(new QLabel(tr("Startup behavior:")), ++row, 0); for (int behavior = 0; behavior < BaseSettings::LastSentinel ; ++behavior) m_startupBehavior->addItem(startupBehaviorString(BaseSettings::StartBehavior(behavior))); m_startupBehavior->setCurrentIndex(settings->m_startBehavior); @@ -650,8 +650,8 @@ QString BaseSettingsWidget::name() const LanguageFilter BaseSettingsWidget::filter() const { - return {m_mimeTypes->text().split(filterSeparator), - m_filePattern->text().split(filterSeparator)}; + return {m_mimeTypes->text().split(filterSeparator, QString::SkipEmptyParts), + m_filePattern->text().split(filterSeparator, QString::SkipEmptyParts)}; } BaseSettings::StartBehavior BaseSettingsWidget::startupBehavior() const @@ -784,10 +784,10 @@ QString StdIOSettingsWidget::arguments() const bool LanguageFilter::isSupported(const Utils::FilePath &filePath, const QString &mimeType) const { - if (mimeTypes.isEmpty() && filePattern.isEmpty()) - return true; if (mimeTypes.contains(mimeType)) return true; + if (filePattern.isEmpty() && filePath.isEmpty()) + return mimeTypes.isEmpty(); auto regexps = Utils::transform(filePattern, [](const QString &pattern){ return QRegExp(pattern, Utils::HostOsInfo::fileNameCaseSensitivity(), QRegExp::Wildcard); }); diff --git a/src/plugins/perfprofiler/perfprofilertool.cpp b/src/plugins/perfprofiler/perfprofilertool.cpp index 5af097a9ec..bb71f925da 100644 --- a/src/plugins/perfprofiler/perfprofilertool.cpp +++ b/src/plugins/perfprofiler/perfprofilertool.cpp @@ -157,6 +157,9 @@ PerfProfilerTool::PerfProfilerTool() tracePointsAction->setEnabled(m_startAction->isEnabled()); }); + connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions, + this, &PerfProfilerTool::updateRunActions); + m_recordButton = new QToolButton; m_clearButton = new QToolButton; m_filterButton = new QToolButton; @@ -341,9 +344,6 @@ void PerfProfilerTool::createViews() menu1->exec(m_flameGraphView->mapToGlobal(pos)); }); - connect(ProjectExplorerPlugin::instance(), &ProjectExplorerPlugin::updateRunActions, - this, &PerfProfilerTool::updateRunActions); - m_perspective.addToolBarAction(m_startAction); m_perspective.addToolBarAction(m_stopAction); m_perspective.addToolBarWidget(m_recordButton); diff --git a/src/plugins/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 7631f26cac..df9fd503ba 100644 --- a/src/plugins/projectexplorer/appoutputpane.cpp +++ b/src/plugins/projectexplorer/appoutputpane.cpp @@ -213,6 +213,7 @@ AppOutputPane::AppOutputPane() : connect(this, &Core::IOutputPane::zoomIn, this, &AppOutputPane::zoomIn); connect(this, &Core::IOutputPane::zoomOut, this, &AppOutputPane::zoomOut); + connect(this, &IOutputPane::resetZoom, this, &AppOutputPane::resetZoom); m_settingsButton->setToolTip(tr("Open Settings Page")); m_settingsButton->setIcon(Utils::Icons::SETTINGS_TOOLBAR.icon()); @@ -247,6 +248,7 @@ AppOutputPane::AppOutputPane() : setupFilterUi("AppOutputPane.Filter"); setFilteringEnabled(false); setZoomButtonsEnabled(false); + setupContext("Core.AppOutputPane", m_mainWidget); } AppOutputPane::~AppOutputPane() @@ -662,6 +664,12 @@ void AppOutputPane::zoomOut(int range) tab.window->zoomOut(range); } +void AppOutputPane::resetZoom() +{ + for (const RunControlTab &tab : qAsConst(m_runControlTabs)) + tab.window->resetZoom(); +} + void AppOutputPane::enableButtons(const RunControl *rc) { if (rc) { diff --git a/src/plugins/projectexplorer/appoutputpane.h b/src/plugins/projectexplorer/appoutputpane.h index 5fc88219fc..6d7936042e 100644 --- a/src/plugins/projectexplorer/appoutputpane.h +++ b/src/plugins/projectexplorer/appoutputpane.h @@ -120,6 +120,7 @@ private: void zoomIn(int range); void zoomOut(int range); + void resetZoom(); void enableButtons(const RunControl *rc); diff --git a/src/plugins/projectexplorer/compileoutputwindow.cpp b/src/plugins/projectexplorer/compileoutputwindow.cpp index 91cc2fb22c..d92bcc4bbe 100644 --- a/src/plugins/projectexplorer/compileoutputwindow.cpp +++ b/src/plugins/projectexplorer/compileoutputwindow.cpp @@ -177,6 +177,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : connect(this, &IOutputPane::zoomIn, m_outputWindow, &Core::OutputWindow::zoomIn); connect(this, &IOutputPane::zoomOut, m_outputWindow, &Core::OutputWindow::zoomOut); + connect(this, &IOutputPane::resetZoom, m_outputWindow, &Core::OutputWindow::resetZoom); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, this, updateFontSettings); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, @@ -194,6 +195,7 @@ CompileOutputWindow::CompileOutputWindow(QAction *cancelBuildAction) : m_handler = new ShowOutputTaskHandler(this); ExtensionSystem::PluginManager::addObject(m_handler); + setupContext(C_COMPILE_OUTPUT, m_outputWindow); loadSettings(); updateFromSettings(); } diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index 673aa61cfc..43758e0a36 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -381,7 +381,7 @@ void EnvironmentWidget::amendPathList(const PathListModifier &modifier) { const QString varName = d->m_model->indexToVariable(d->m_environmentView->currentIndex()); const QString dir = QDir::toNativeSeparators( - QFileDialog::getExistingDirectory(this, tr("Choose a directory"))); + QFileDialog::getExistingDirectory(this, tr("Choose Directory"))); if (dir.isEmpty()) return; QModelIndex index = d->m_model->variableToIndex(varName); diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp index 418ae03564..12bd7ccf13 100644 --- a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp @@ -92,7 +92,7 @@ class FilterKitAspectsModel : public TreeModel<TreeItem, FilterTreeItem> public: FilterKitAspectsModel(const Kit *kit, QObject *parent) : TreeModel(parent) { - setHeader({tr("Setting"), tr("Visible")}); + setHeader({FilterKitAspectsDialog::tr("Setting"), FilterKitAspectsDialog::tr("Visible")}); for (const KitAspect * const aspect : KitManager::kitAspects()) { if (kit && !aspect->isApplicableToKit(kit)) continue; diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.h b/src/plugins/projectexplorer/filterkitaspectsdialog.h index 441abd658d..f59a58f683 100644 --- a/src/plugins/projectexplorer/filterkitaspectsdialog.h +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.h @@ -37,6 +37,7 @@ namespace Internal { class FilterKitAspectsDialog : public QDialog { + Q_OBJECT public: FilterKitAspectsDialog(const Kit *kit, QWidget *parent); QSet<Core::Id> irrelevantAspects() const; diff --git a/src/plugins/projectexplorer/importwidget.cpp b/src/plugins/projectexplorer/importwidget.cpp index eac2c44c94..33db7d48df 100644 --- a/src/plugins/projectexplorer/importwidget.cpp +++ b/src/plugins/projectexplorer/importwidget.cpp @@ -63,10 +63,14 @@ ImportWidget::ImportWidget(QWidget *parent) : connect(importButton, &QAbstractButton::clicked, this, &ImportWidget::handleImportRequest); connect(m_pathChooser->lineEdit(), &QLineEdit::returnPressed, this, [this] { if (m_pathChooser->isValid()) { + m_ownsReturnKey = true; handleImportRequest(); // The next return should trigger the "Configure" button. - QTimer::singleShot(0, this, QOverload<>::of(&QWidget::setFocus)); + QTimer::singleShot(0, this, [this] { + setFocus(); + m_ownsReturnKey = false; + }); } }); @@ -79,9 +83,9 @@ void ImportWidget::setCurrentDirectory(const Utils::FilePath &dir) m_pathChooser->setFileName(dir); } -bool ImportWidget::lineEditHasFocus() const +bool ImportWidget::ownsReturnKey() const { - return m_pathChooser->lineEdit()->hasFocus(); + return m_ownsReturnKey; } void ImportWidget::handleImportRequest() diff --git a/src/plugins/projectexplorer/importwidget.h b/src/plugins/projectexplorer/importwidget.h index 8999bc117d..5c63beecdb 100644 --- a/src/plugins/projectexplorer/importwidget.h +++ b/src/plugins/projectexplorer/importwidget.h @@ -44,7 +44,7 @@ public: void setCurrentDirectory(const Utils::FilePath &dir); - bool lineEditHasFocus() const; + bool ownsReturnKey() const; signals: void importFrom(const Utils::FilePath &dir); @@ -53,6 +53,7 @@ private: void handleImportRequest(); Utils::PathChooser *m_pathChooser; + bool m_ownsReturnKey = false; }; } // namespace Internal diff --git a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index 7141148ef8..7684de0795 100644 --- a/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp +++ b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp @@ -104,7 +104,7 @@ public: : QDialog(parent), m_view(new Utils::TreeView(this)) { setWindowTitle(QCoreApplication::translate("ProjectExplorer::JsonWizard", - "Choose project file")); + "Choose Project File")); const auto model = new ProjectFilesModel(candidates, this); m_view->setSelectionMode(Utils::TreeView::ExtendedSelection); m_view->setSelectionBehavior(Utils::TreeView::SelectRows); @@ -121,7 +121,7 @@ public: const auto layout = new QVBoxLayout(this); layout->addWidget(new QLabel(QCoreApplication::translate("ProjectExplorer::JsonWizard", "The project contains more than one project file. " - "Please select the one you would like to use."))); + "Select the one you would like to use."))); layout->addWidget(m_view); layout->addWidget(buttonBox); } diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 15f345dc5b..60cd9a6e21 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1298,6 +1298,7 @@ MsvcToolChainConfigWidget::MsvcToolChainConfigWidget(ToolChain *tc) m_mainLayout->removeRow(m_mainLayout->rowCount() - 1); QHBoxLayout *hLayout = new QHBoxLayout(); + m_varsBatPathCombo->setObjectName("varsBatCombo"); m_varsBatPathCombo->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); m_varsBatPathCombo->setEditable(true); for (const MsvcToolChain *tmpTc : g_availableMsvcToolchains) { @@ -1442,6 +1443,7 @@ ClangClToolChainConfigWidget::ClangClToolChainConfigWidget(ToolChain *tc) : { m_mainLayout->removeRow(m_mainLayout->rowCount() - 1); + m_varsBatDisplayCombo->setObjectName("varsBatCombo"); m_varsBatDisplayCombo->setSizeAdjustPolicy(QComboBox::AdjustToContents); m_mainLayout->addRow(tr("Initialization:"), m_varsBatDisplayCombo); diff --git a/src/plugins/projectexplorer/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp index 5b6015b5a5..7b0d3758e6 100644 --- a/src/plugins/projectexplorer/parseissuesdialog.cpp +++ b/src/plugins/projectexplorer/parseissuesdialog.cpp @@ -71,14 +71,14 @@ ParseIssuesDialog::ParseIssuesDialog(QWidget *parent) : QDialog(parent), d(new P d->clearTasksCheckBox.setText(tr("Clear existing tasks")); d->clearTasksCheckBox.setChecked(true); - const auto loadFileButton = new QPushButton(tr("Load from file...")); + const auto loadFileButton = new QPushButton(tr("Load from File...")); connect(loadFileButton, &QPushButton::clicked, this, [this] { const QString filePath = QFileDialog::getOpenFileName(this, tr("Choose File")); if (filePath.isEmpty()) return; QFile file(filePath); if (!file.open(QIODevice::ReadOnly)) { - QMessageBox::critical(this, tr("Could not open file"), + QMessageBox::critical(this, tr("Could Not Open File"), tr("Could not open file: \"%1\": %2") .arg(filePath, file.errorString())); return; @@ -116,7 +116,7 @@ ParseIssuesDialog::ParseIssuesDialog(QWidget *parent) : QDialog(parent), d(new P // TODO: Only very few parsers are available from a Kit (basically just the Toolchain one). // If we introduced factories for IOutputParsers, we could offer the user // to combine arbitrary parsers here. - const auto parserGroupBox = new QGroupBox(tr("Parsing options")); + const auto parserGroupBox = new QGroupBox(tr("Parsing Options")); layout->addWidget(parserGroupBox); const auto parserLayout = new QVBoxLayout(parserGroupBox); const auto kitChooserWidget = new QWidget; @@ -153,7 +153,7 @@ void ParseIssuesDialog::accept() { std::unique_ptr<IOutputParser> parser(d->kitChooser.currentKit()->createOutputParser()); if (!parser) { - QMessageBox::critical(this, tr("Cannot parse"), tr("Cannot parse: The chosen kit does " + QMessageBox::critical(this, tr("Cannot Parse"), tr("Cannot parse: The chosen kit does " "not provide an output parser.")); return; } diff --git a/src/plugins/projectexplorer/project.cpp b/src/plugins/projectexplorer/project.cpp index 420d6dad32..f34547cc95 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -667,7 +667,7 @@ void Project::changeRootProjectDirectory() { Utils::FilePath rootPath = Utils::FilePath::fromString( QFileDialog::getExistingDirectory(Core::ICore::dialogParent(), - tr("Select The Root Directory"), + tr("Select the Root Directory"), rootProjectDirectory().toString(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks)); @@ -679,7 +679,7 @@ void Project::changeRootProjectDirectory() } /*! - Returns the common root directory that contains all files which belongs to a project. + Returns the common root directory that contains all files which belong to a project. */ Utils::FilePath Project::rootProjectDirectory() const { diff --git a/src/plugins/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index d5c5fb9ce5..2f029ccedd 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1780,7 +1780,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project) void ProjectExplorerPluginPrivate::closeAllProjects() { - if (!EditorManager::closeAllEditors()) + if (!EditorManager::closeAllDocuments()) return; // Action has been cancelled SessionManager::closeAllProjects(); @@ -1935,15 +1935,15 @@ void ProjectExplorerPluginPrivate::setStartupProject(Project *project) bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project) { QTC_ASSERT(project, return false); - QList<IDocument *> openFiles = DocumentModel::openedDocuments(); - Utils::erase(openFiles, [project](const IDocument *doc) { - return !project->isKnownFile(doc->filePath()); + QList<DocumentModel::Entry *> openFiles = DocumentModel::entries(); + Utils::erase(openFiles, [project](const DocumentModel::Entry *entry) { + return entry->pinned || !project->isKnownFile(entry->fileName()); }); for (const Project * const otherProject : SessionManager::projects()) { if (otherProject == project) continue; - Utils::erase(openFiles, [otherProject](const IDocument *doc) { - return otherProject->isKnownFile(doc->filePath()); + Utils::erase(openFiles, [otherProject](const DocumentModel::Entry *entry) { + return otherProject->isKnownFile(entry->fileName()); }); } return EditorManager::closeDocuments(openFiles); @@ -3470,7 +3470,7 @@ void ProjectExplorerPluginPrivate::addExistingProjects() QTC_ASSERT(projectNode, return); const QString dir = directoryFor(currentNode); QStringList subProjectFilePaths = QFileDialog::getOpenFileNames( - ICore::mainWindow(), tr("Please choose a project file"), dir, + ICore::mainWindow(), tr("Choose Project File"), dir, projectNode->subProjectFileNamePatterns().join(";;")); if (!ProjectTree::hasNode(projectNode)) return; diff --git a/src/plugins/projectexplorer/projectmodels.cpp b/src/plugins/projectexplorer/projectmodels.cpp index 839f021f2b..08010bb50b 100644 --- a/src/plugins/projectexplorer/projectmodels.cpp +++ b/src/plugins/projectexplorer/projectmodels.cpp @@ -457,7 +457,7 @@ public: : m_buttonBox(new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)), m_buttonGroup(new QButtonGroup(this)) { - setWindowTitle(tr("Please choose a drop action")); + setWindowTitle(tr("Choose Drop Action")); const bool offerFileIo = !defaultTargetDir.isEmpty(); auto * const layout = new QVBoxLayout(this); layout->addWidget(new QLabel(tr("You just dragged some files from one project node to " @@ -469,8 +469,8 @@ public: m_buttonGroup->addButton(moveButton, int(DropAction::Move)); layout->addWidget(moveButton); if (offerFileIo) { - copyButton->setText(tr("Copy only the file references")); - moveButton->setText(tr("Move only the file references")); + copyButton->setText(tr("Copy Only File References")); + moveButton->setText(tr("Move Only File References")); auto * const copyWithFilesButton = new QRadioButton(tr("Copy file references and files"), this); m_buttonGroup->addButton(copyWithFilesButton, int(DropAction::CopyWithFiles)); @@ -506,8 +506,8 @@ public: } }); } else { - copyButton->setText(tr("Copy the file references")); - moveButton->setText(tr("Move the file references")); + copyButton->setText(tr("Copy File References")); + moveButton->setText(tr("Move File References")); moveButton->setChecked(true); } connect(m_buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); diff --git a/src/plugins/projectexplorer/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index c0dba5c020..0006d7678c 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -332,7 +332,7 @@ void TargetSetupPage::setProjectImporter(ProjectImporter *importer) bool TargetSetupPage::importLineEditHasFocus() const { - return m_importWidget->lineEditHasFocus(); + return m_importWidget->ownsReturnKey(); } void TargetSetupPage::setNoteText(const QString &text) diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp index 623b20a1a4..95b7c8eb5b 100644 --- a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp +++ b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp @@ -164,7 +164,7 @@ KitAspect::ItemList QmakeKitAspect::toUserOutput(const Kit *k) const void QmakeKitAspect::addToMacroExpander(Kit *kit, MacroExpander *expander) const { - expander->registerVariable("Qmake:mkspec", tr("Mkspec configured for qmake by the Kit."), + expander->registerVariable("Qmake:mkspec", tr("Mkspec configured for qmake by the kit."), [kit]() -> QString { return QDir::toNativeSeparators(mkspec(kit)); }); diff --git a/src/plugins/qmakeprojectmanager/qmakesettings.cpp b/src/plugins/qmakeprojectmanager/qmakesettings.cpp index f3c2bf9760..37c0a4ce39 100644 --- a/src/plugins/qmakeprojectmanager/qmakesettings.cpp +++ b/src/plugins/qmakeprojectmanager/qmakesettings.cpp @@ -103,7 +103,7 @@ public: m_warnAgainstUnalignedBuildDirCheckbox.setText(tr("Warn if a project's source and " "build directories are not at the same level")); m_warnAgainstUnalignedBuildDirCheckbox.setToolTip(tr("Qmake has subtle bugs that " - "can trigger if source and build directory are not at the same level.")); + "can be triggered if source and build directory are not at the same level.")); m_warnAgainstUnalignedBuildDirCheckbox.setChecked( QmakeSettings::warnAgainstUnalignedBuildDir()); m_alwaysRunQmakeCheckbox.setText(tr("Run qmake on every build")); diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 028d3a21b4..0535a8f52e 100644 --- a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h +++ b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h @@ -165,6 +165,7 @@ const int priorityGenericToolBar = 50; const int priorityLast = 60; const char addImagesDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Image Files"); +const char addFontsDisplayString[] = QT_TRANSLATE_NOOP("QmlDesignerAddResources", "Font Files"); } //ComponentCoreConstants diff --git a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp index 6edbacec36..37e1fdfd53 100644 --- a/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp +++ b/src/plugins/qmldesigner/components/componentcore/designeractionmanager.cpp @@ -1026,6 +1026,13 @@ void DesignerActionManager::createDefaultAddResourceHandler() registerAddResourceHandler(AddResourceHandler(ComponentCoreConstants::addImagesDisplayString, "*.svg", ModelNodeOperations::addImageToProject)); + + registerAddResourceHandler(AddResourceHandler(ComponentCoreConstants::addFontsDisplayString, + "*.ttf", + ModelNodeOperations::addFontToProject)); + registerAddResourceHandler(AddResourceHandler(ComponentCoreConstants::addFontsDisplayString, + "*.otf", + ModelNodeOperations::addFontToProject)); } void DesignerActionManager::addDesignerAction(ActionInterface *newAction) diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp index 3c31998576..9f880c453b 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.cpp @@ -966,6 +966,38 @@ void addTabBarToStackedContainer(const SelectionContext &selectionContext) } +bool addFontToProject(const QStringList &fileNames, const QString &defaultDirectory) +{ + QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory); + + if (directory.isEmpty()) + return true; + + bool allSuccessful = true; + for (const QString &fileName : fileNames) { + const QString targetFile = directory + "/" + QFileInfo(fileName).fileName(); + const bool success = QFile::copy(fileName, targetFile); + + auto document = QmlDesignerPlugin::instance()->currentDesignDocument(); + + QTC_ASSERT(document, return false); + + if (success) { + ProjectExplorer::Node *node = ProjectExplorer::ProjectTree::nodeForFile(document->fileName()); + if (node) { + ProjectExplorer::FolderNode *containingFolder = node->parentFolderNode(); + if (containingFolder) + containingFolder->addFiles(QStringList(targetFile)); + } + } else { + allSuccessful = false; + } + } + + return allSuccessful; +} + + bool addImageToProject(const QStringList &fileNames, const QString &defaultDirectory) { QString directory = AddImagesDialog::getDirectory(fileNames, defaultDirectory); diff --git a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h index e3396a303e..52dfaf6f1d 100644 --- a/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h +++ b/src/plugins/qmldesigner/components/componentcore/modelnodeoperations.h @@ -73,6 +73,7 @@ void increaseIndexOfStackedContainer(const SelectionContext &selectionContext); void decreaseIndexOfStackedContainer(const SelectionContext &selectionContext); void addTabBarToStackedContainer(const SelectionContext &selectionContext); bool addImageToProject(const QStringList &fileNames, const QString &directory); +bool addFontToProject(const QStringList &fileNames, const QString &directory); } // namespace ModelNodeOperationso } //QmlDesigner diff --git a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h index cfffeeaca4..f686631079 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h @@ -27,6 +27,7 @@ #include <qmlitemnode.h> +#include <QDir> #include <QObject> #include <QStringList> #include <QUrl> diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index 4263a1e498..aec85826a2 100644 --- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp +++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp @@ -256,6 +256,11 @@ QList<DeployableFile> QnxDeployQtLibrariesDialog::gatherFiles( if (dirPath.isEmpty()) return result; + static const QStringList unusedDirs = {"include", "mkspecs", "cmake", "pkgconfig"}; + const QString dp = dirPath.endsWith('/') ? dirPath.left(dirPath.size() - 1) : dirPath; + if (unusedDirs.contains(dp)) + return result; + QDir dir(dirPath); QFileInfoList list = dir.entryInfoList(nameFilters, QDir::Dirs | QDir::Files | QDir::NoDotAndDotDot); @@ -265,6 +270,10 @@ QList<DeployableFile> QnxDeployQtLibrariesDialog::gatherFiles( result.append(gatherFiles(fileInfo.absoluteFilePath(), baseDirPath.isEmpty() ? dirPath : baseDirPath)); } else { + static const QStringList unusedSuffixes = {"cmake", "la", "prl", "a", "pc"}; + if (unusedSuffixes.contains(fileInfo.suffix())) + continue; + QString remoteDir; if (baseDirPath.isEmpty()) { remoteDir = fullRemoteDirectory() + QLatin1Char('/') + diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 96c41218e8..8af1a6aebd 100644 --- a/src/plugins/remotelinux/makeinstallstep.cpp +++ b/src/plugins/remotelinux/makeinstallstep.cpp @@ -118,13 +118,13 @@ bool MakeInstallStep::init() } QDir rootDir(rootDirPath); if (cleanInstallRoot() && !rootDir.removeRecursively()) { - emit addTask(Task(Task::Error, tr("The install root '%1' could not be cleaned.") + emit addTask(Task(Task::Error, tr("The install root \"%1\" could not be cleaned.") .arg(installRoot().toUserOutput()), FilePath(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM)); return false; } if (!rootDir.exists() && !QDir::root().mkpath(rootDirPath)) { - emit addTask(Task(Task::Error, tr("The install root '%1' could not be created.") + emit addTask(Task(Task::Error, tr("The install root \"%1\" could not be created.") .arg(installRoot().toUserOutput()), FilePath(), -1, Constants::TASK_CATEGORY_BUILDSYSTEM)); return false; diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index d1f7dbc1bf..bbeff2a484 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -33,6 +33,7 @@ #include <coreplugin/icore.h> #include <coreplugin/messagemanager.h> #include <utils/mimetypes/mimedatabase.h> +#include <utils/qtcassert.h> #include <DefinitionDownloader> #include <Format> @@ -119,6 +120,8 @@ Highlighter::Definition Highlighter::definitionForDocument(const TextDocument *d Highlighter::Definition Highlighter::definitionForMimeType(const QString &mimeType) { + if (mimeType.isEmpty()) + return {}; const Definitions definitions = definitionsForMimeType(mimeType); if (definitions.size() == 1) return definitions.first(); @@ -140,13 +143,23 @@ Highlighter::Definition Highlighter::definitionForName(const QString &name) Highlighter::Definitions Highlighter::definitionsForDocument(const TextDocument *document) { - const Utils::MimeType mimeType = Utils::mimeTypeForName(document->mimeType()); - Definitions definitions; - if (mimeType.isValid()) - definitions = Highlighter::definitionsForMimeType(mimeType.name()); - if (definitions.isEmpty()) - definitions = Highlighter::definitionsForFileName(document->filePath()); - return definitions; + QTC_ASSERT(document, return {}); + const Utils::MimeType &mimeType = Utils::mimeTypeForName(document->mimeType()); + if (mimeType.isValid()) { + if (mimeType.name() == "text/plain") { + // text/plain is the base mime type for all text types so ignore it and try matching the + // file name against the pattern and only if no definition can be found for the + // file name try matching the mime type + const Definitions &fileNameDefinitions = definitionsForFileName(document->filePath()); + if (!fileNameDefinitions.isEmpty()) + return fileNameDefinitions; + return definitionsForMimeType(mimeType.name()); + } + const Definitions &mimeTypeDefinitions = definitionsForMimeType(mimeType.name()); + if (!mimeTypeDefinitions.isEmpty()) + return mimeTypeDefinitions; + } + return definitionsForFileName(document->filePath()); } static Highlighter::Definition definitionForSetting(const QString &settingsKey, @@ -193,6 +206,7 @@ Highlighter::Definitions Highlighter::definitionsForFileName(const Utils::FilePa void Highlighter::rememberDefintionForDocument(const Highlighter::Definition &definition, const TextDocument *document) { + QTC_ASSERT(document, return ); if (!definition.isValid()) return; const QString &mimeType = document->mimeType(); diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index 2d7a98457d..6a699c4686 100644 --- a/src/plugins/valgrind/memchecktool.cpp +++ b/src/plugins/valgrind/memchecktool.cpp @@ -630,7 +630,7 @@ MemcheckToolPrivate::MemcheckToolPrivate() }); action = new QAction(this); - action->setText(tr("Valgrind Memory Analyzer with GDB")); + action->setText(MemcheckTool::tr("Valgrind Memory Analyzer with GDB")); action->setToolTip(MemcheckTool::tr("Valgrind Analyze Memory with GDB uses the " "Memcheck tool to find memory leaks.\nWhen a problem is detected, " "the application is interrupted and can be debugged.")); @@ -650,7 +650,7 @@ MemcheckToolPrivate::MemcheckToolPrivate() } else { action = new QAction(MemcheckTool::tr("Heob"), this); Core::Command *cmd = Core::ActionManager::registerAction(action, "Memcheck.Local"); - cmd->setDefaultKeySequence(QKeySequence(tr("Ctrl+Alt+H"))); + cmd->setDefaultKeySequence(QKeySequence(MemcheckTool::tr("Ctrl+Alt+H"))); connect(action, &QAction::triggered, this, &MemcheckToolPrivate::heobAction); menu->addAction(cmd, Debugger::Constants::G_ANALYZER_TOOLS); connect(m_startAction, &QAction::changed, action, [action, this] { @@ -659,7 +659,7 @@ MemcheckToolPrivate::MemcheckToolPrivate() } action = new QAction(this); - action->setText(tr("Valgrind Memory Analyzer (External Application)")); + action->setText(MemcheckTool::tr("Valgrind Memory Analyzer (External Application)")); action->setToolTip(toolTip); menu->addAction(ActionManager::registerAction(action, "Memcheck.Remote"), Debugger::Constants::G_ANALYZER_REMOTE_TOOLS); @@ -724,7 +724,7 @@ void MemcheckToolPrivate::heobAction() } } if (!hasLocalRc) { - const QString msg = tr("Heob: No local run configuration available."); + const QString msg = MemcheckTool::tr("Heob: No local run configuration available."); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); return; @@ -733,7 +733,7 @@ void MemcheckToolPrivate::heobAction() || abi.os() != Abi::WindowsOS || abi.binaryFormat() != Abi::PEFormat || (abi.wordWidth() != 32 && abi.wordWidth() != 64)) { - const QString msg = tr("Heob: No toolchain available."); + const QString msg = MemcheckTool::tr("Heob: No toolchain available."); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); return; @@ -746,7 +746,7 @@ void MemcheckToolPrivate::heobAction() // target executable if (executable.isEmpty()) { - const QString msg = tr("Heob: No executable set."); + const QString msg = MemcheckTool::tr("Heob: No executable set."); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); return; @@ -754,7 +754,7 @@ void MemcheckToolPrivate::heobAction() if (!QFile::exists(executable)) executable = Utils::HostOsInfo::withExecutableSuffix(executable); if (!QFile::exists(executable)) { - const QString msg = tr("Heob: Cannot find %1.").arg(executable); + const QString msg = MemcheckTool::tr("Heob: Cannot find %1.").arg(executable); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); return; @@ -775,9 +775,11 @@ void MemcheckToolPrivate::heobAction() const QString heob = QString("heob%1.exe").arg(abi.wordWidth()); const QString heobPath = dialog.path() + '/' + heob; if (!QFile::exists(heobPath)) { - QMessageBox::critical(Core::ICore::mainWindow(), tr("Heob"), - tr("The %1 executables must be in the appropriate location.") - .arg("<a href=\"https://github.com/ssbssa/heob/releases\">Heob</a>")); + QMessageBox::critical( + Core::ICore::mainWindow(), + MemcheckTool::tr("Heob"), + MemcheckTool::tr("The %1 executables must be in the appropriate location.") + .arg("<a href=\"https://github.com/ssbssa/heob/releases\">Heob</a>")); return; } @@ -786,13 +788,19 @@ void MemcheckToolPrivate::heobAction() const QString dwarfstack = QString("dwarfstack%1.dll").arg(abi.wordWidth()); const QString dwarfstackPath = dialog.path() + '/' + dwarfstack; if (!QFile::exists(dwarfstackPath) - && CheckableMessageBox::doNotShowAgainInformation( - Core::ICore::mainWindow(), tr("Heob"), - tr("Heob used with MinGW projects needs the %1 DLLs for proper stacktrace resolution.") - .arg("<a href=\"https://github.com/ssbssa/dwarfstack/releases\">Dwarfstack</a>"), - ICore::settings(), "HeobDwarfstackInfo", - QDialogButtonBox::Ok | QDialogButtonBox::Cancel, - QDialogButtonBox::Ok) != QDialogButtonBox::Ok) + && CheckableMessageBox::doNotShowAgainInformation( + Core::ICore::mainWindow(), + MemcheckTool::tr("Heob"), + MemcheckTool::tr("Heob used with MinGW projects needs the %1 DLLs for proper " + "stacktrace resolution.") + .arg( + "<a " + "href=\"https://github.com/ssbssa/dwarfstack/releases\">Dwarfstack</a>"), + ICore::settings(), + "HeobDwarfstackInfo", + QDialogButtonBox::Ok | QDialogButtonBox::Cancel, + QDialogButtonBox::Ok) + != QDialogButtonBox::Ok) return; } @@ -837,7 +845,9 @@ void MemcheckToolPrivate::heobAction() CREATE_UNICODE_ENVIRONMENT | CREATE_SUSPENDED | CREATE_NEW_CONSOLE, envPtr, reinterpret_cast<LPCWSTR>(workingDirectory.utf16()), &si, &pi)) { DWORD e = GetLastError(); - const QString msg = tr("Heob: Cannot create %1 process (%2).").arg(heob).arg(qt_error_string(e)); + const QString msg = MemcheckTool::tr("Heob: Cannot create %1 process (%2).") + .arg(heob) + .arg(qt_error_string(e)); TaskHub::addTask(Task::Error, msg, Debugger::Constants::ANALYZERTASK_ID); TaskHub::requestPopup(); return; diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index d5b4a48b8e..da3cb8fa29 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -239,7 +239,7 @@ void OutputWindowPlainTextEdit::appendLinesWithStyle(const QString &s, setFormat(style); if (style == VcsOutputWindow::Command) { - const QString timeStamp = QTime::currentTime().toString("\nHH:mm "); + const QString timeStamp = QTime::currentTime().toString("\nHH:mm:ss "); appendLines(timeStamp + s, repository); } else { appendLines(s, repository); @@ -291,22 +291,17 @@ VcsOutputWindow::VcsOutputWindow() Q_ASSERT(d->passwordRegExp.isValid()); m_instance = this; - auto updateFontSettings = [] { - d->widget.setBaseFont(TextEditor::TextEditorSettings::fontSettings().font()); - }; - auto updateBehaviorSettings = [] { d->widget.setWheelZoomEnabled( TextEditor::TextEditorSettings::behaviorSettings().m_scrollWheelZooming); }; - updateFontSettings(); updateBehaviorSettings(); + setupContext(Internal::C_VCS_OUTPUT_PANE, &d->widget); connect(this, &IOutputPane::zoomIn, &d->widget, &Core::OutputWindow::zoomIn); connect(this, &IOutputPane::zoomOut, &d->widget, &Core::OutputWindow::zoomOut); - connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, - this, updateFontSettings); + connect(this, &IOutputPane::resetZoom, &d->widget, &Core::OutputWindow::resetZoom); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, this, updateBehaviorSettings); } diff --git a/src/shared/proparser/qmakeevaluator.cpp b/src/shared/proparser/qmakeevaluator.cpp index 01233fc46f..6e75847eb9 100644 --- a/src/shared/proparser/qmakeevaluator.cpp +++ b/src/shared/proparser/qmakeevaluator.cpp @@ -1702,7 +1702,7 @@ QMakeEvaluator::VisitReturn QMakeEvaluator::evaluateFunction( if (m_valuemapStack.count() >= 100) { evalError(fL1S("Ran into infinite recursion (depth > 100).")); - vr = ReturnFalse; + vr = ReturnError; } else { m_valuemapStack.push(ProValueMap()); m_locationStack.push(m_current); diff --git a/src/shared/qbs b/src/shared/qbs -Subproject 59cc3f249b1f507afda1757e6edd1ba25970fa1 +Subproject e0ac40e53c5fc16b675ea2ac63fb5f3a6cab3de diff --git a/src/shared/registryaccess/registryaccess.h b/src/shared/registryaccess/registryaccess.h index 2880aa6677..cfd945d35a 100644 --- a/src/shared/registryaccess/registryaccess.h +++ b/src/shared/registryaccess/registryaccess.h @@ -45,6 +45,7 @@ enum AccessMode { static const char *debuggerApplicationFileC = "qtcdebugger"; static const WCHAR *debuggerRegistryKeyC = L"Software\\Microsoft\\Windows NT\\CurrentVersion\\AeDebug"; static const WCHAR *debuggerRegistryValueNameC = L"Debugger"; +static const WCHAR *autoRegistryValueNameC = L"Auto"; static inline QString wCharToQString(const WCHAR *w) { diff --git a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index 0ce49e829d..bcf05e5d04 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -31,6 +31,7 @@ #include <environment.h> #include <executeinloop.h> #include <filepathcaching.h> +#include <filesystem.h> #include <generatedfiles.h> #include <modifiedtimechecker.h> #include <pchcreator.h> @@ -179,7 +180,8 @@ struct Data // because we have a cycle dependency Sqlite::Database database; ClangBackEnd::RefactoringDatabaseInitializer<Sqlite::Database> databaseInitializer{database}; ClangBackEnd::FilePathCaching filePathCache{database}; - ClangPathWatcher<QFileSystemWatcher, QTimer> includeWatcher{filePathCache}; + ClangBackEnd::FileSystem fileSystem{filePathCache}; + ClangPathWatcher<QFileSystemWatcher, QTimer> includeWatcher{filePathCache, fileSystem}; ApplicationEnvironment environment; ProjectPartsStorage<> projectPartsStorage{database}; PrecompiledHeaderStorage<> preCompiledHeaderStorage{database}; @@ -212,12 +214,7 @@ struct Data // because we have a cycle dependency ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache, generatedFiles, environment}; - std::function<TimeStamp(FilePathView filePath)> getModifiedTime{ - [&](ClangBackEnd::FilePathView path) -> TimeStamp { - return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); - }}; - ClangBackEnd::ModifiedTimeChecker<ClangBackEnd::SourceEntries> modifiedTimeChecker{getModifiedTime, - filePathCache}; + ClangBackEnd::ModifiedTimeChecker<ClangBackEnd::SourceEntries> modifiedTimeChecker{fileSystem}; ClangBackEnd::BuildDependenciesProvider buildDependencyProvider{buildDependencyStorage, modifiedTimeChecker, buildDependencyCollector, diff --git a/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h b/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h index 8c8c51a6cd..187a27cf89 100644 --- a/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h +++ b/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h @@ -66,10 +66,9 @@ public: } } - FilePathIds fetchSources(ProjectPartId projectPartId) const override + FilePathIds fetchPchSources(ProjectPartId projectPartId) const override { - return fetchProjectPartsFilesStatement.template values<FilePathId>(1024, - projectPartId.projectPathId); + return fetchPchSourcesStatement.template values<FilePathId>(1024, projectPartId.projectPathId); } void insertOrUpdateFileStatuses(const FileStatuses &fileStatuses) override @@ -252,8 +251,10 @@ public: "CONFLICT(sourceId, projectPartId) DO UPDATE SET sourceType = ?003, " "hasMissingIncludes = ?004", database}; - mutable ReadStatement fetchProjectPartsFilesStatement{ - "SELECT sourceId FROM projectPartsFiles WHERE projectPartId = ? ORDER BY sourceId", database}; + mutable ReadStatement fetchPchSourcesStatement{ + "SELECT sourceId FROM projectPartsFiles WHERE projectPartId = ? AND sourceType IN (0, 1, " + "3, 4) ORDER BY sourceId", + database}; mutable ReadStatement fetchSourceDependenciesStatement{ "WITH RECURSIVE collectedDependencies(sourceId) AS (VALUES(?) UNION " "SELECT dependencySourceId FROM sourceDependencies, " diff --git a/src/tools/clangpchmanagerbackend/source/builddependenciesstorageinterface.h b/src/tools/clangpchmanagerbackend/source/builddependenciesstorageinterface.h index 1166564b32..a1c20811aa 100644 --- a/src/tools/clangpchmanagerbackend/source/builddependenciesstorageinterface.h +++ b/src/tools/clangpchmanagerbackend/source/builddependenciesstorageinterface.h @@ -56,7 +56,7 @@ public: virtual UsedMacros fetchUsedMacros(FilePathId sourceId) const = 0; virtual ProjectPartId fetchProjectPartId(Utils::SmallStringView projectPartName) = 0; virtual void updatePchCreationTimeStamp(long long pchCreationTimeStamp, ProjectPartId projectPartId) = 0; - virtual FilePathIds fetchSources(ProjectPartId projectPartId) const = 0; + virtual FilePathIds fetchPchSources(ProjectPartId projectPartId) const = 0; protected: ~BuildDependenciesStorageInterface() = default; diff --git a/src/tools/clangpchmanagerbackend/source/pchcreator.cpp b/src/tools/clangpchmanagerbackend/source/pchcreator.cpp index cd7b492868..15c9a19547 100644 --- a/src/tools/clangpchmanagerbackend/source/pchcreator.cpp +++ b/src/tools/clangpchmanagerbackend/source/pchcreator.cpp @@ -114,7 +114,7 @@ void PchCreator::generatePch(PchTask &&pchTask) { m_projectPartPch.projectPartId = pchTask.projectPartId(); m_projectPartPch.lastModified = QDateTime::currentSecsSinceEpoch(); - + m_sources = std::move(pchTask.sources); if (pchTask.includes.empty()) return; @@ -127,10 +127,9 @@ void PchCreator::generatePch(PchTask &&pchTask) m_clangTool.addFile(std::move(headerFilePath), content.clone(), std::move(commandLine)); bool success = generatePch(NativeFilePath{headerFilePath}, content); - if (success) { - m_sources = pchTask.sources; - m_projectPartPch.pchPath = std::move(pchOutputPath); - } + + if (success) + m_projectPartPch.pchPath = std::move(pchOutputPath); } const ProjectPartPch &PchCreator::projectPartPch() diff --git a/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h b/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h index 9db3c683b9..2d16b484a1 100644 --- a/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h +++ b/src/tools/clangpchmanagerbackend/source/usedmacrofilter.h @@ -128,23 +128,26 @@ private: case SourceType::TopSystemInclude: topSystemIncludes.emplace_back(source.sourceId); systemIncludes.emplace_back(source.sourceId); + sources.emplace_back(source.sourceId); break; case SourceType::SystemInclude: systemIncludes.emplace_back(source.sourceId); + sources.emplace_back(source.sourceId); break; case SourceType::TopProjectInclude: topProjectIncludes.emplace_back(source.sourceId); projectIncludes.emplace_back(source.sourceId); + sources.emplace_back(source.sourceId); break; case SourceType::ProjectInclude: projectIncludes.emplace_back(source.sourceId); + sources.emplace_back(source.sourceId); break; case SourceType::UserInclude: case SourceType::Source: break; } - sources.emplace_back(source.sourceId); } static Utils::SmallStringVector filterUsedMarcos(const UsedMacros &usedMacros, diff --git a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri index e7e83da788..a3da84d83b 100644 --- a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri +++ b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri @@ -17,8 +17,6 @@ HEADERS += \ $$PWD/symbolsvisitorbase.h \ $$PWD/usedmacro.h \ $$PWD/sourcedependency.h \ - $$PWD/filestatus.h \ - $$PWD/filestatuscache.h \ $$PWD/indexdataconsumer.h \ $$PWD/sourcesmanager.h \ $$PWD/symbolindexertaskqueue.h \ @@ -67,5 +65,4 @@ HEADERS += \ SOURCES += \ $$PWD/filestatuspreprocessorcallbacks.cpp \ $$PWD/sourcerangefilter.cpp \ - $$PWD/symbolindexer.cpp \ - $$PWD/filestatuscache.cpp + $$PWD/symbolindexer.cpp diff --git a/src/tools/clangrefactoringbackend/source/filestatuscache.cpp b/src/tools/clangrefactoringbackend/source/filestatuscache.cpp deleted file mode 100644 index 14ad48da14..0000000000 --- a/src/tools/clangrefactoringbackend/source/filestatuscache.cpp +++ /dev/null @@ -1,93 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2017 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 "filestatuscache.h" - -#include <QDateTime> -#include <QFileInfo> - -namespace ClangBackEnd { - -FileStatusCache::FileStatusCache(FilePathCachingInterface &filePathCache) - : m_filePathCache(filePathCache) -{ - -} - -long long FileStatusCache::lastModifiedTime(FilePathId filePathId) const -{ - return findEntry(filePathId).lastModified; -} - -void FileStatusCache::update(FilePathId filePathId) -{ - auto found = std::lower_bound(m_cacheEntries.begin(), - m_cacheEntries.end(), - Internal::FileStatusCacheEntry{filePathId}, - [] (const auto &first, const auto &second) { - return first.filePathId < second.filePathId; - }); - - if (found != m_cacheEntries.end() && found->filePathId == filePathId) { - QFileInfo fileInfo = qFileInfo(filePathId); - found->lastModified = fileInfo.lastModified().toMSecsSinceEpoch() / 1000; - } -} - -FileStatusCache::size_type FileStatusCache::size() const -{ - return m_cacheEntries.size(); -} - -Internal::FileStatusCacheEntry FileStatusCache::findEntry(FilePathId filePathId) const -{ - auto found = std::lower_bound(m_cacheEntries.begin(), - m_cacheEntries.end(), - Internal::FileStatusCacheEntry{filePathId}, - [] (const auto &first, const auto &second) { - return first.filePathId < second.filePathId; - }); - - if (found != m_cacheEntries.end() && found->filePathId == filePathId) - return *found; - - QFileInfo fileInfo = qFileInfo(filePathId); - auto inserted = m_cacheEntries.emplace(found, - filePathId, - fileInfo.lastModified().toMSecsSinceEpoch() / 1000); - - return *inserted; -} - -QFileInfo FileStatusCache::qFileInfo(FilePathId filePathId) const -{ - QFileInfo fileInfo(QString(m_filePathCache.filePath(filePathId))); - - fileInfo.refresh(); - - return fileInfo; -} - -} // namespace ClangBackEnd diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp index 1ee61eb67c..4f3a6f172f 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp @@ -87,8 +87,8 @@ SymbolIndexer::SymbolIndexer(SymbolIndexerTaskQueueInterface &symbolIndexerTaskQ void SymbolIndexer::updateProjectParts(ProjectPartContainers &&projectParts) { - for (ProjectPartContainer &projectPart : projectParts) - updateProjectPart(std::move(projectPart)); + for (ProjectPartContainer &projectPart : projectParts) + updateProjectPart(std::move(projectPart)); } void SymbolIndexer::updateProjectPart(ProjectPartContainer &&projectPart) @@ -145,7 +145,7 @@ void SymbolIndexer::updateProjectPart(ProjectPartContainer &&projectPart) } m_pathWatcher.updateIdPaths( - {{projectPartId, m_buildDependencyStorage.fetchSources(projectPartId)}}); + {{projectPartId, m_buildDependencyStorage.fetchPchSources(projectPartId)}}); m_symbolIndexerTaskQueue.addOrUpdateTasks(std::move(symbolIndexerTask)); m_symbolIndexerTaskQueue.processEntries(); } @@ -154,6 +154,8 @@ void SymbolIndexer::pathsWithIdsChanged(const ProjectPartIds &) {} void SymbolIndexer::pathsChanged(const FilePathIds &filePathIds) { + m_modifiedTimeChecker.pathsChanged(filePathIds); + FilePathIds dependentSourcePathIds = m_symbolStorage.fetchDependentSourceIds(filePathIds); std::vector<SymbolIndexerTask> symbolIndexerTask; diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.h b/src/tools/clangrefactoringbackend/source/symbolindexer.h index 64b442bf33..d969cd8026 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.h @@ -29,8 +29,8 @@ #include "symbolindexertaskqueueinterface.h" #include "symbolstorageinterface.h" #include "builddependenciesstorageinterface.h" -#include "clangpathwatcher.h" +#include <clangpathwatcher.h> #include <filecontainerv2.h> #include <modifiedtimecheckerinterface.h> #include <precompiledheaderstorageinterface.h> diff --git a/src/tools/clangrefactoringbackend/source/symbolindexing.h b/src/tools/clangrefactoringbackend/source/symbolindexing.h index 5c391503bf..936a34af00 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexing.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexing.h @@ -39,6 +39,7 @@ #include <projectpartsstorage.h> #include <filepathcachingfwd.h> +#include <filesystem.h> #include <modifiedtimechecker.h> #include <refactoringdatabaseinitializer.h> @@ -142,16 +143,12 @@ private: PrecompiledHeaderStorage<Sqlite::Database> m_precompiledHeaderStorage; ProjectPartsStorage<Sqlite::Database> m_projectPartsStorage; SymbolStorage m_symbolStorage; - ClangPathWatcher<QFileSystemWatcher, QTimer> m_sourceWatcher{m_filePathCache}; - FileStatusCache m_fileStatusCache{m_filePathCache}; + FileSystem m_fileSytem{m_filePathCache}; + ClangPathWatcher<QFileSystemWatcher, QTimer> m_sourceWatcher{m_filePathCache, m_fileSytem}; + FileStatusCache m_fileStatusCache{m_fileSytem}; SymbolsCollectorManager m_collectorManger; ProgressCounter m_progressCounter; - std::function<TimeStamp(FilePathView filePath)> getModifiedTime{ - [&](ClangBackEnd::FilePathView path) -> TimeStamp { - return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); - }}; - ModifiedTimeChecker<ClangBackEnd::SourceTimeStamps> m_modifiedTimeChecker{getModifiedTime, - m_filePathCache}; + ModifiedTimeChecker<ClangBackEnd::SourceTimeStamps> m_modifiedTimeChecker{m_fileSytem}; SymbolIndexer m_indexer; SymbolIndexerTaskQueue m_indexerQueue; SymbolIndexerTaskScheduler m_indexerScheduler; diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index 071837ac52..ab5a00fc6b 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp @@ -129,7 +129,11 @@ bool SymbolsCollector::collectSymbols() auto actionFactory = ClangBackEnd::newFrontendActionFactory(&m_collectSymbolsAction); - return tool.run(actionFactory.get()) != 1; + bool noErrors = tool.run(actionFactory.get()) != 1; + + m_clangTool = ClangTool(); + + return noErrors; } void SymbolsCollector::doInMainThreadAfterFinished() diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.h b/src/tools/clangrefactoringbackend/source/symbolscollector.h index cc7ca4396c..e103471372 100644 --- a/src/tools/clangrefactoringbackend/source/symbolscollector.h +++ b/src/tools/clangrefactoringbackend/source/symbolscollector.h @@ -63,6 +63,8 @@ public: bool isUsed() const override; void setIsUsed(bool isUsed) override; + bool isClean() const { return m_clangTool.isClean(); } + private: FilePathCaching m_filePathCache; ClangTool m_clangTool; diff --git a/src/tools/qtcdebugger/main.cpp b/src/tools/qtcdebugger/main.cpp index 38507c8539..6beda7c1ec 100644 --- a/src/tools/qtcdebugger/main.cpp +++ b/src/tools/qtcdebugger/main.cpp @@ -419,6 +419,15 @@ static bool registerDebuggerKey(const WCHAR *key, do { if (!openRegistryKey(HKEY_LOCAL_MACHINE, key, true, &handle, access, errorMessage)) break; + + // Make sure to automatically open the qtcdebugger dialog on a crash + QString autoVal; + registryReadStringKey(handle, autoRegistryValueNameC, &autoVal, errorMessage); + if (autoVal != "1") { + if (!registryWriteStringKey(handle, autoRegistryValueNameC, "1", errorMessage)) + break; + } + // Save old key, which might be missing QString oldDebugger; if (isRegistered(handle, call, errorMessage, &oldDebugger)) { |