From 8ae890c0675e66c3d606b9c38f4b7c5c12c656fa Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Thu, 20 Jun 2019 16:47:59 +0200 Subject: [PATCH 01/59] CppEditor: Check model index in ParseContextModel::data Task-number: QTCREATORBUG-22596 Change-Id: I6babbab8fc9c66ca6d16fe60d93bfbc9147ead90 Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- src/plugins/cppeditor/cppparsecontext.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cppeditor/cppparsecontext.cpp b/src/plugins/cppeditor/cppparsecontext.cpp index 7c98fbba8f7..cf77ffe8088 100644 --- a/src/plugins/cppeditor/cppparsecontext.cpp +++ b/src/plugins/cppeditor/cppparsecontext.cpp @@ -118,7 +118,7 @@ int ParseContextModel::rowCount(const QModelIndex &) const 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(); From 7c572287a719c96bbcbd8728fbcc0f26d9e00f35 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 21 Jun 2019 12:15:55 +0200 Subject: [PATCH 02/59] ProjectExplorer: Respect pinned documents ... in "Close All Projects and Editors". Amends fe21a7a77e. Change-Id: I162753e08f97cf4538bf58e5f48fd222cdb9ee2b Reviewed-by: Eike Ziller --- src/plugins/coreplugin/editormanager/editormanager.cpp | 8 ++++---- src/plugins/coreplugin/editormanager/editormanager.h | 4 ++-- src/plugins/projectexplorer/projectexplorer.cpp | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/plugins/coreplugin/editormanager/editormanager.cpp b/src/plugins/coreplugin/editormanager/editormanager.cpp index 813d12739d0..f1073e6bb2c 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 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 &entries) +bool EditorManager::closeDocuments(const QList &entries) { QList documentsToClose; for (DocumentModel::Entry *entry : entries) { @@ -2642,7 +2642,7 @@ void EditorManager::closeDocuments(const QList &entries) else documentsToClose << entry->document; } - closeDocuments(documentsToClose); + return closeDocuments(documentsToClose); } bool EditorManager::closeEditors(const QList &editorsToClose, bool askAboutModifiedEditors) diff --git a/src/plugins/coreplugin/editormanager/editormanager.h b/src/plugins/coreplugin/editormanager/editormanager.h index 41429aedb11..5be379ceb25 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 &documents, bool askAboutModifiedEditors = true); static void closeDocument(DocumentModel::Entry *entry); - static void closeDocuments(const QList &entries); + static bool closeDocuments(const QList &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/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 9d0e1181ad9..01b5bba1f79 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1747,7 +1747,7 @@ void ProjectExplorerPlugin::unloadProject(Project *project) void ProjectExplorerPluginPrivate::closeAllProjects() { - if (!EditorManager::closeAllEditors()) + if (!EditorManager::closeAllDocuments()) return; // Action has been cancelled SessionManager::closeAllProjects(); From 2a9996e9163608c9047f0c2775347d86b153f9f0 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 21 Jun 2019 12:37:20 +0200 Subject: [PATCH 03/59] Update qbs submodule MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To HEAD of 1.14 branch. Change-Id: I662bab89006041b54a9d467a4aa40b4035732ece Reviewed-by: Jörg Bornemann --- src/shared/qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/qbs b/src/shared/qbs index 79695499bbd..e0ac40e53c5 160000 --- a/src/shared/qbs +++ b/src/shared/qbs @@ -1 +1 @@ -Subproject commit 79695499bbdd0eb804dd7e15d571481ae47d6f25 +Subproject commit e0ac40e53c5fc16b675ea2ac63fb5f3a6cab3dee From acf301fa13334bc8bbc078e00cddafe2b0eb7938 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Fri, 21 Jun 2019 11:58:28 +0200 Subject: [PATCH 04/59] Doc: Add information about new CMake features Change-Id: Iae561d5d13dc45e83432b0515679702b6b4737e1 Reviewed-by: Cristian Adam Reviewed-by: Orgad Shaneh --- doc/images/qtcreator-cmakeexecutable.png | Bin 28996 -> 11541 bytes doc/src/cmake/creator-projects-cmake.qdoc | 7 ++++++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/images/qtcreator-cmakeexecutable.png b/doc/images/qtcreator-cmakeexecutable.png index 8b328b0fb974e6c6861126971d90ec31f8a1557b..a0475e0c042463e7c286ad071e9624ba68d8c83b 100644 GIT binary patch literal 11541 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 diff --git a/doc/src/cmake/creator-projects-cmake.qdoc b/doc/src/cmake/creator-projects-cmake.qdoc index 2a29cac9c1d..585031ded78 100644 --- a/doc/src/cmake/creator-projects-cmake.qdoc +++ b/doc/src/cmake/creator-projects-cmake.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -43,6 +43,9 @@ native build configurations and workspaces that you can use in the compiler environment of your choice. + You can use CMake from \QC to build applications for the desktop and + Android devices. You can also build single files to test your changes. + \QC automatically detects the CMake executable specified in the \c PATH. You can add paths to other CMake executables and use them in different build and run \l{glossary-buildandrun-kit}{kits}. @@ -109,6 +112,8 @@ \li Code completion + \li Path completion + \li Auto-indentation \li Matching parentheses and quotes From d57e052768929c50506f250441e89bddafb5d452 Mon Sep 17 00:00:00 2001 From: hjk Date: Fri, 21 Jun 2019 13:51:56 +0200 Subject: [PATCH 05/59] Qnx: Do not deploy files that are not needed Task-number: QTCREATORBUG-20000 Change-Id: Ie9a2341a3bcd1531bc83b54bf43466df1347627c Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp index 4263a1e4985..aec85826a2f 100644 --- a/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp +++ b/src/plugins/qnx/qnxdeployqtlibrariesdialog.cpp @@ -256,6 +256,11 @@ QList 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 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('/') + From a47975b8faf23e27024692433f871adb6c6b5a9a Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 21 Jun 2019 13:10:21 +0200 Subject: [PATCH 06/59] CMake build: Do not write versions etc into the cache The user is not supposed to change these via configuration. If they are written to the cache, version bumps do not take effect automatically, resulting in quite some hassle. Change-Id: Ibaf9dba02114da1cbc3ec2210ae7c3328f35bb1f Reviewed-by: Cristian Adam Reviewed-by: Eike Ziller --- CMakeLists.txt | 43 +++++++++++++++++++++---------------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2280aee8231..73800c329de 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,23 +5,22 @@ include(FeatureSummary) #BINARY_ARTIFACTS_BRANCH = master #PROJECT_USER_FILE_EXTENSION = .user -set(IDE_VERSION "4.9.83" CACHE STRING "The IDE version.") -set(IDE_VERSION_COMPAT "4.9.83" CACHE STRING "The IDE Compatibility version.") -set(IDE_VERSION_DISPLAY "4.10.0-beta2" CACHE STRING "The IDE display version.") -set(IDE_COPYRIGHT_YEAR "2019" CACHE STRING "The IDE copyright year.") +set(IDE_VERSION "4.9.83") # The IDE version. +set(IDE_VERSION_COMPAT "4.9.83") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.10.0-beta2") # The IDE display version. +set(IDE_COPYRIGHT_YEAR "2019") # The IDE copyright year. + +set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. +set(IDE_COPY_SETTINGSVARIANT "Nokia") # The IDE settings to initially import. +set(IDE_DISPLAY_NAME "Qt Creator") # The IDE display name. +set(IDE_ID "qtcreator") # The IDE id (no spaces, lowercase!) +set(IDE_CASED_ID "QtCreator") # The cased IDE id (no spaces!) +set(IDE_BUNDLE_IDENTIFIER "org.qt-project.${IDE_ID}") # The macOS application bundle identifier. set(IDE_REVISION FALSE CACHE BOOL "Marks the presence of IDE revision string.") set(IDE_REVISION_STR "" CACHE STRING "The IDE revision string.") -set(IDE_SETTINGSVARIANT "QtProject" CACHE STRING "The IDE settings variation.") -set(IDE_COPY_SETTINGSVARIANT "Nokia" CACHE STRING "The IDE settings to initially import.") -set(IDE_DISPLAY_NAME "Qt Creator" CACHE STRING "The IDE display name.") -set(IDE_ID "qtcreator" CACHE STRING "The IDE id (no spaces, lowercase!)") -set(IDE_CASED_ID "QtCreator" CACHE STRING "The cased IDE id (no spaces!)") -set(IDE_BUNDLE_IDENTIFIER "org.qt-project.${IDE_ID}" CACHE STRING "The macOS application bundle identifier.") -mark_as_advanced(IDE_VERSION_COMPAT IDE_VERSION_DISPLAY IDE_COPYRIGHT_YEAR - IDE_REVISION IDE_REVISION_STR IDE_SETTINGSVARIANT IDE_COPY_SETTINGSVARIANT - IDE_DISPLAY_NAME IDE_ID IDE_CASED_ID IDE_BUNDLE_IDENTIFIER) +mark_as_advanced(IDE_REVISION IDE_REVISION_STR) project(QtCreator VERSION ${IDE_VERSION}) @@ -154,15 +153,15 @@ else () set(_IDE_BIN_PATH "bin") endif () -set(IDE_APP_PATH "${_IDE_APP_PATH}" CACHE PATH "The target path of the IDE application (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_APP_TARGET "${_IDE_APP_TARGET}" CACHE PATH "The IDE application name.") -set(IDE_PLUGIN_PATH "${_IDE_PLUGIN_PATH}" CACHE PATH "The IDE plugin path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_LIBRARY_BASE_PATH "${_IDE_LIBRARY_BASE_PATH}" CACHE PATH "The IDE library base path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_LIBRARY_PATH "${_IDE_LIBRARY_PATH}" CACHE PATH "The IDE library path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_LIBEXEC_PATH "${_IDE_LIBEXEC_PATH}" CACHE PATH "The IDE libexec path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_DATA_PATH "${_IDE_DATA_PATH}" CACHE PATH "The IDE data path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_DOC_PATH "${_IDE_DOC_PATH}" CACHE PATH "The IDE documentation path (relative to CMAKE_INSTALL_PREFIX).") -set(IDE_BIN_PATH "${_IDE_BIN_PATH}" CACHE PATH "The IDE bin path (relative to CMAKE_INSTALL_PREFIX).") +set(IDE_APP_PATH "${_IDE_APP_PATH}") # The target path of the IDE application (relative to CMAKE_INSTALL_PREFIX). +set(IDE_APP_TARGET "${_IDE_APP_TARGET}") # The IDE application name. +set(IDE_PLUGIN_PATH "${_IDE_PLUGIN_PATH}") # The IDE plugin path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_LIBRARY_BASE_PATH "${_IDE_LIBRARY_BASE_PATH}") # The IDE library base path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_LIBRARY_PATH "${_IDE_LIBRARY_PATH}") # The IDE library path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_LIBEXEC_PATH "${_IDE_LIBEXEC_PATH}") # The IDE libexec path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_DATA_PATH "${_IDE_DATA_PATH}") # The IDE data path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_DOC_PATH "${_IDE_DOC_PATH}") # The IDE documentation path (relative to CMAKE_INSTALL_PREFIX). +set(IDE_BIN_PATH "${_IDE_BIN_PATH}") # The IDE bin path (relative to CMAKE_INSTALL_PREFIX). file(RELATIVE_PATH RELATIVE_PLUGIN_PATH "/${IDE_BIN_PATH}" "/${IDE_PLUGIN_PATH}") file(RELATIVE_PATH RELATIVE_LIBEXEC_PATH "/${IDE_BIN_PATH}" "/${IDE_LIBEXEC_PATH}") From f4acc2f1629a6b323b8d263521949a54b89841f4 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Fri, 21 Jun 2019 14:40:34 +0200 Subject: [PATCH 07/59] CMake build: Allow branding Create your own QtCreatorIDEBranding.cmake somewhere and point cmake to it via CMAKE_MODULE_PATH Task-number: QTCREATORBUG-22488 Change-Id: Ic1057d879c5104b57e4ed8ef8a9c4fc8d4140de9 Reviewed-by: Cristian Adam Reviewed-by: Eike Ziller --- CMakeLists.txt | 26 ++++++-------------------- cmake/QtCreatorIDEBranding.cmake | 14 ++++++++++++++ 2 files changed, 20 insertions(+), 20 deletions(-) create mode 100644 cmake/QtCreatorIDEBranding.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 73800c329de..46ab6b8da70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,21 +1,10 @@ cmake_minimum_required(VERSION 3.9) +## Add paths to check for cmake modules: +list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") + include(FeatureSummary) - -#BINARY_ARTIFACTS_BRANCH = master -#PROJECT_USER_FILE_EXTENSION = .user - -set(IDE_VERSION "4.9.83") # The IDE version. -set(IDE_VERSION_COMPAT "4.9.83") # The IDE Compatibility version. -set(IDE_VERSION_DISPLAY "4.10.0-beta2") # The IDE display version. -set(IDE_COPYRIGHT_YEAR "2019") # The IDE copyright year. - -set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. -set(IDE_COPY_SETTINGSVARIANT "Nokia") # The IDE settings to initially import. -set(IDE_DISPLAY_NAME "Qt Creator") # The IDE display name. -set(IDE_ID "qtcreator") # The IDE id (no spaces, lowercase!) -set(IDE_CASED_ID "QtCreator") # The cased IDE id (no spaces!) -set(IDE_BUNDLE_IDENTIFIER "org.qt-project.${IDE_ID}") # The macOS application bundle identifier. +include(QtCreatorIDEBranding) set(IDE_REVISION FALSE CACHE BOOL "Marks the presence of IDE revision string.") set(IDE_REVISION_STR "" CACHE STRING "The IDE revision string.") @@ -24,9 +13,6 @@ mark_as_advanced(IDE_REVISION IDE_REVISION_STR) project(QtCreator VERSION ${IDE_VERSION}) -## Add paths to check for cmake modules: -list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") - # Force C++ standard, do not fall back, do not use compiler extensions set(CMAKE_CXX_STANDARD 14) set(CMAKE_CXX_STANDARD_REQUIRED ON) @@ -126,7 +112,7 @@ endif() set(_IDE_APP_PATH "bin") if (APPLE) - set(_IDE_APP_TARGET "Qt Creator") + set(_IDE_APP_TARGET "${IDE_DISPLAY_NAME}") set(_IDE_OUTPUT_PATH "${_IDE_APP_PATH}/${_IDE_APP_TARGET}.app/Contents") @@ -138,7 +124,7 @@ if (APPLE) set(_IDE_DOC_PATH "${_IDE_OUTPUT_PATH}/Resources/doc") set(_IDE_BIN_PATH "${_IDE_OUTPUT_PATH}/MacOS") else () - set(_IDE_APP_TARGET "qtcreator") + set(_IDE_APP_TARGET "${IDE_ID}") set(_IDE_LIBRARY_BASE_PATH "lib") set(_IDE_LIBRARY_PATH "lib/qtcreator") diff --git a/cmake/QtCreatorIDEBranding.cmake b/cmake/QtCreatorIDEBranding.cmake new file mode 100644 index 00000000000..f7327d19fa5 --- /dev/null +++ b/cmake/QtCreatorIDEBranding.cmake @@ -0,0 +1,14 @@ +#BINARY_ARTIFACTS_BRANCH = master +#PROJECT_USER_FILE_EXTENSION = .user + +set(IDE_VERSION "4.9.83") # The IDE version. +set(IDE_VERSION_COMPAT "4.9.83") # The IDE Compatibility version. +set(IDE_VERSION_DISPLAY "4.10.0-beta2") # The IDE display version. +set(IDE_COPYRIGHT_YEAR "2019") # The IDE copyright year. + +set(IDE_SETTINGSVARIANT "QtProject") # The IDE settings variation. +set(IDE_COPY_SETTINGSVARIANT "Nokia") # The IDE settings to initially import. +set(IDE_DISPLAY_NAME "Qt Creator") # The IDE display name. +set(IDE_ID "qtcreator") # The IDE id (no spaces, lowercase!) +set(IDE_CASED_ID "QtCreator") # The cased IDE id (no spaces!) +set(IDE_BUNDLE_IDENTIFIER "org.qt-project.${IDE_ID}") # The macOS application bundle identifier. From a5402165499830416b70f290e680cb44e93193d1 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Fri, 21 Jun 2019 14:44:43 +0200 Subject: [PATCH 08/59] QmlJS: Add "Array" to global functions This fixes a false positive error message. Task-number: QTCREATORBUG-22599 Change-Id: I54104857982873baaa092ad936586c3d245ce720 Reviewed-by: Tim Jenssen --- src/libs/qmljs/qmljscheck.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libs/qmljs/qmljscheck.cpp b/src/libs/qmljs/qmljscheck.cpp index deb95ea1d3d..e5243830cdb 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); From 7ceadd4c3fc9e2ee55036cfbfe2b883a0b027043 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Fri, 21 Jun 2019 14:28:00 +0200 Subject: [PATCH 09/59] ProjectExplorer: Respect pinned documents ...when closing the files of a project. Also remove unused member IDocumentPrivate::pinned. Amends fe21a7a77e. Change-Id: I15fa06bcde2022c559c4bd7234625c5ea06ba26e Reviewed-by: Eike Ziller --- src/plugins/coreplugin/idocument.cpp | 1 - src/plugins/projectexplorer/projectexplorer.cpp | 10 +++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/plugins/coreplugin/idocument.cpp b/src/plugins/coreplugin/idocument.cpp index 8fde404064e..aab9845f1a5 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/projectexplorer/projectexplorer.cpp b/src/plugins/projectexplorer/projectexplorer.cpp index 01b5bba1f79..834fd26b53f 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -1902,15 +1902,15 @@ void ProjectExplorerPluginPrivate::setStartupProject(Project *project) bool ProjectExplorerPluginPrivate::closeAllFilesInProject(const Project *project) { QTC_ASSERT(project, return false); - QList openFiles = DocumentModel::openedDocuments(); - Utils::erase(openFiles, [project](const IDocument *doc) { - return !project->isKnownFile(doc->filePath()); + QList 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); From 3717ca9574d82dd42405ebff14243111c078cf2a Mon Sep 17 00:00:00 2001 From: David Schulz Date: Wed, 19 Jun 2019 09:57:33 +0200 Subject: [PATCH 10/59] ensure the 'Auto' key is set while registering post mortem debugger The 'Auto' indicates that the the registered post mortem debugger is directly started after an unhandled user exception. If the value is unset or set to '0' in rescent windows versions the debugger selection dialog is not shown. Set this value to '1 ' to show the qtcdebugger dialog, which also has an option to show the system dialog, if the user does not want to debug the exception with Qt Creator. Change-Id: I3160315060dbfb37bec5eaa677c4208900f574a4 Reviewed-by: Cristian Adam Reviewed-by: Christian Stenger --- src/shared/registryaccess/registryaccess.h | 1 + src/tools/qtcdebugger/main.cpp | 9 +++++++++ 2 files changed, 10 insertions(+) diff --git a/src/shared/registryaccess/registryaccess.h b/src/shared/registryaccess/registryaccess.h index 2880aa66772..cfd945d35a1 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/qtcdebugger/main.cpp b/src/tools/qtcdebugger/main.cpp index 38507c85394..6beda7c1ecb 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)) { From 6dde7767ce0e117d1281a7c7922b282793ba5871 Mon Sep 17 00:00:00 2001 From: David Schulz Date: Fri, 21 Jun 2019 14:16:54 +0200 Subject: [PATCH 11/59] Highlighter: Use file pattern matching if mime type is 'text/plain' Do not try to match 'text/plain' mime types against highlight definitions, because it is automatically set if the file contains just printable characters in the first bytes, in this case file pattern matching gives better results. Fixes: QTCREATORBUG-22540 Change-Id: Ifd662cd6961011f5cf5d9232ce5f17d2314b4824 Reviewed-by: Eike Ziller --- src/plugins/texteditor/highlighter.cpp | 28 +++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/plugins/texteditor/highlighter.cpp b/src/plugins/texteditor/highlighter.cpp index d1f7dbc1bf0..bbeff2a4844 100644 --- a/src/plugins/texteditor/highlighter.cpp +++ b/src/plugins/texteditor/highlighter.cpp @@ -33,6 +33,7 @@ #include #include #include +#include #include #include @@ -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(); From 517cb44bb0e93720b41a8a62301251e79d39317c Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Tue, 18 Jun 2019 11:23:03 +0200 Subject: [PATCH 12/59] qmake: Let evaluateFunction() return error for infinite recursion MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Otherwise, it can happen that parsing goes on forever in cumulative mode. Task-number: QTCREATORBUG-17656 Reviewed-by: Jörg Bornemann (cherry-picked from qtbase/feb06decfe5355a14c982f7ddb427a262ad2b393) Change-Id: If69f2265ac7eee0d230bd77a9aa9500e97ebeff6 Reviewed-by: Jörg Bornemann --- src/shared/proparser/qmakeevaluator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/shared/proparser/qmakeevaluator.cpp b/src/shared/proparser/qmakeevaluator.cpp index 01233fc46fd..6e75847eb9c 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); From 864e3ab44aa676c4254fa5a58146a642f481c958 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Thu, 20 Jun 2019 12:12:33 +0200 Subject: [PATCH 13/59] Doc: Describe creating and running Boost UTF tests Task-number: QTCREATORBUG-21169 Change-Id: I5e90d2cd089dbfac2a49a33c69ba0fa2ac43438b Reviewed-by: Christian Stenger --- .../qtcreator-autotests-options-boost.png | Bin 0 -> 5636 bytes .../qtcreator-autotests-options-google.png | Bin 15199 -> 7267 bytes doc/images/qtcreator-autotests-options-qt.png | Bin 17817 -> 7429 bytes doc/images/qtcreator-autotests-options.png | Bin 7194 -> 12098 bytes doc/images/qtcreator-autotests.png | Bin 69724 -> 44003 bytes doc/images/qtcreator-tests-view.png | Bin 30506 -> 8796 bytes .../howto/creator-only/creator-autotest.qdoc | 97 +++++++++++++++--- .../creator-only/creator-overview.qdoc | 6 +- .../creator-only/creator-testing.qdoc | 7 +- 9 files changed, 87 insertions(+), 23 deletions(-) create mode 100644 doc/images/qtcreator-autotests-options-boost.png diff --git a/doc/images/qtcreator-autotests-options-boost.png b/doc/images/qtcreator-autotests-options-boost.png new file mode 100644 index 0000000000000000000000000000000000000000..4e1f8c43617bc3cd100f93fb1c7376e97f599b4b GIT binary patch literal 5636 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 bMlh&OaFIu z#%H-%S?YCq_;jheAD^nT_$u4!;-n748x2QHS)9jA-#e%#W2 z<<^-GUwUReN(yrF%FFZh^>t)Z+aRPnd7T-{4_DXYFBbio^ym`jxwLq@4I4IZHrw&~ z)`psS6@NBdKWbMN9zJRPaaQ%EExTiX-d1+E-o4!InxSWkevFvt+ovh_-p|?^o#GO7 z)asK@ex{#~$A+!tLZRt%QzywAs$4MN@q=}i)ap~cYDe7#CkrofUc?o4VNTWM{ z$jqilFs%~(bLe$<(vBVsGvkn%9O>W7c5SBD9!Ku z(y#c)#%3{l=_HM_J!+FUTYlVlv0}%g(3G9u?o82eYFQgP;a$DH?BR+fc^!_@3=B0Y zw;b{u?n;O+wVPdA^r#_Fs&_m)<3*#{Ozd-h#_FLi&< z+k0-YdY#^xGu&98JBEeS9DZ`}sO6c^$4XIGjxTD<*v+n{7Pf7f*t)`IVm}lCF$cb2GJd-zD6}24BfJ;Gc@DqLGRi%;ge%*x|XP?zwNAJ zocj4q@3R9PX(7&5GqP1&m#L_yr!7-=Px-RhJS*n?U2`XCYk}pFU7vlQc3U?7-;w&~ z%&8kU_BL#@pP2Cfhnbh=*DKqXt>*jvnLoiXEnmXAgJ z&s+a(R02XS>||s(;J%1=30H7%@Rzjs{QUPXUc7kl;KAzaf4jQ7Wo2aI)>W*U_DQlt zt96t9McWGb(@)oTS*cETTNtn+hcRlwg9|G|tb#QHGef*~I)$GN;OeWG6uvWJDvwm_ zIk!%R^C4WVQLD4x1-`P2dGX3+^|aHa(Q~ybt3$Z{KJZWrUmd#QJnLKE>yAo~jW2BE zbLHywQVn0x=l?amuF$Xs8i#YoW)w zt8)skM?FsVbP?BX-cdI5(T3ehGetxbvYt-tY-4%Rzv9wWEfcL)uiAY@JesF&z6$hd z4&W^Zm6@GpSF%du;_hva|L%O`$dN7EwjFx<`{A1MrH=yd^3LC?<&t^TygQlsf=k-d zHP3vt*OxAPP@|x^N(C1fa`}?={uJ?j!4ga3LaCw0ctR@-j~3ey1Q(?+;S+l_|D?#RtG$=EM6q4`@_GOL4LzFc-p@^0EE2u-W1~b) z#&aQewWZs`uP$>>{?n8+J;1>2T}%&sn>RgL6wH?C7! zza=kd!4z*jPw5qHjfYC6O`m@K>ebNj@Ws_}>rF1OG3*J?eY2d6P5r8#;SKMJGd5g` zR4d+d^869er+bQP-ighh`S8yRPP?spjIRDZ_xQl1Q(X?m{98`kyQ8-4U2B{1WXbkV zUuND@y29BnR+zTz-9pRa>PeqZv2ADg|7WG~%Hl0Ef85~pS7F<6*|wT%8zpMb{t(>AnE$={JiQTPsEQ@xrc07 zvip|Rv5;l$Gk+ev7VdU`Z_{CN+@{3totTLn@#7WRTw9Hy{r1q z!F98Tj^L*9-?P2CrI)jC-CUZ%eD|k~9RFONtmT)E+q!+_F=78J6R_SQgPGype6teI z#Q)!}uGO#mztj5}&#ZapKL0)~|97k)JHma?#R_3Qe7UmyNl zw0y~;g+~`Em2=0=+jrl#!z$q2^W#gm=z7M#3%eRWb7GLYZQ`|if6m&kl?|A7Ks|1v zYA>scx^Rr=DON9L6z`N;snXi#mv^sISJruVlmGMOeOm;jgL{gY=9P#oeZI|j z%5Bx~`oBxpHqV{j@*Y2IW_T~Hi1v1MvvaKrqbn83I?X@il zN^y#j{F%>n}guw`9_^Cj#!9Cw%&LQ1#Vr`8l=!T=bou8YP#m zSXSBfe{(kT*Tg+io~@JQUxzk27=LwMb-_a`M6i*GiQ!(da_C>3>895o{4uGSqBwIs zXX!VseH#0pPOgi~oSorh_4e}_R`DkfWdkZd7Ej^3d8hELyKWHsTK~0+%8u?{na5~p z`(^s>&@#2Qhr;R=?7VzxKi+n8Jf74WJIlq-w=(I0OxV_K5wCN?kJ#HrEdR80i{o_d z>x*ZF+s#a=cz#R6@#I{;S)K=5-bC_kz4I^A;_sr_v)vB4ndvI!zGxh=Tld{K<7ySHdAp~s z7AsA^qYm=dDW)zOQ=%N>BENh>D7S{rYvi)XD|<%nWr)%;L_! z`>tPOyjxy*wnV&jW7q%5SG!iQKikKgJ^AH2|NKR3D*X3o-O8@v$&ahgNU1rkv#9p( ziv-n^4-LJl(vuzq8Sc6`>D8|jN1p8LJ?XsL+O~ogw^r#jS6%QvS2|pE*1C&g$cmN_MRYf1ay9wD^A9tpDu%=$~%BK|khu zC|^CTSNPoV+Q*A2tv|o6b_~tk$}&Crd+AJ`lTSlEH@=+ft>L^VX_L;MkG8_b(u=Lr z7B1#7&ENFr=UaWb-|a2S`ZioYE&J}C>AvFY+RKwO*V)-bE$`j?F)OGj(Xr>ev-0QV z=l02Zulc)ArMdjBhpNlk5Tg`RD2jbRU_1Pc<$mh$;3F!-vURK5Fg0zdio@ z@3gZ0pJi&KY8NhgDqA43T!V4Gz|)=1mhXRj$rC@_u13|>@Lh05&*wKCQnjCs2v120SR2}1swtAm{#@#s))Jwk z)@zQv+v-6 z_QN3N|Cc;kUr8s`wg{el%6p*pZ>hbBaNKw1hWSf)87lnuaOo}5KccvUgQ3BU;lb}8 zADktX6s9PGcnoNRHlXGNtbK8ynpL&$N}SmX8NYwBAAA`ZGMIlT?D(=|!;4hGlG}^J z%_Z`g&bl952{LNWRitTJg{^S$fSI$WoVPUfn0*qS9`A$#}yFDbSoOPCtwFX@$^mhWsh!R5p8 zCELxWuuuGa>ooTV#n2G5-2Q{EhQX#@OQX8E{tHhPV$BxV&SDjPOROVg*1dr6?Bj=a zZdJSA5G8WhvFLyFbr^->cq-u8C| z!Cf56`nE|ISNvtnOVsstk6IeDY{wIA)u~!3pPGN}Yn$_A&a87QnRohBdERm`&& z^YG!rM~||q&TXlYX87Hnf8Q?4%pG+3 z?%R11cWZ=VZGXN@5qiFV!LGlbD<7`0TYc3qeV_TMt0ymhoLOHaJ-gOml4Nqb-iqG( z!`;f~yk`EL%kJh@{{MN`V~x)n>~Gz?v)89)rTY4Sl`pve_MVN6zCGLbYU*BZo~ce% zn!O<&LMzmpk0vdQXWX^=XN}#fb?(}rJhV6Kv5%vyLfelWiOfvFSjgy%?b%U*weI)}cyE7&q-1Oweqg2MVwmmZ@zR4=ReL%3i`2U~s z??J!5=|v@^ovwAZ_B(I(=U>&|mF^7ZuD>}Ye(s~o`PKC$ay9`5*TY;N70$`m`~79Z zg;OuqdEV-CaJhT)Ps_1=GTrRm1rlrKM;FB0{S)-C__DQsOrgc|{k3UcUTU-cJY1(P z_x#V3zKOLG2k&dHoYqzJU5)K%>DmpN$+=%;wWmuj^^(V7z<-p z#m1WYa=NF0+ivm(Ee|eWt8|Cs%C#rmbh2gG*e-kdr$@%adx7gMCyu750 zpZDy>BjT)5$$JhcZ+QMD=jPc1O6!)a+`KwJDQ)t>n=)@Rdy5M{AG<1dQqAC;WVL#F z%`2z)+=c&x+i!fD>93~zX6Cm=Gd*ul@h;fT^Wl5pr*6%6Z{A2sMa|0cH>h|KdHLGS z%jSEwSqZfCzPsy~U6k~PgZH!i5s&PtVLNWzzId%u==kaI-`2OSo>$thH~X@cw?ule zVaN%A72(W(g|=_`Kk^-?yqjzkvvJ99vCoWpx>C*?3c9d?B{P!R; z#WO7`&CxTowB@mJOpWmW$v>an$dS9&9}{{zV1`52$^2Z8&n2HdUO7z9Oqsdws#wM^ zoqbZiU(Q|pwtMl0Z+4!hO9R96Z*F_P!M?>!YiZanvkTYmn69@A_9?lttuNXNyxC_9XK7(lNu%oHwdLRP zmEE^2nfG47r23`9&r5En7CQZZ@a7xyweB|wJW&#H7q;%S6X`$rgH>pkS?SYzdGS#h zNim*vg%)hm0`s?@`#)u$RqF=5d-AbjGK%%l;m*n7lbuVi8!VckW^Wgh$9ct<<;j;f z^H!DxaY*D?D6USLqCD*lk9p{Jy|_H(sXpt(HiTPcd7tLi{^XT4J%VXzNKL2m%2TIL zZ;W_z?i^naxUlB^^4QSay#Dd)x)-&5@1pXX(4dsO)t-UmuE ze6VYn5dxZbZrJ|daDT(eXO~~&ZKI&T zpeN73z>x7c`$DziM8k@Jxp3T*}S7%98 z^F4j#{O#hY3jg~*)S`;B*R0*O>?5ml-6Gpti$0z^_vH5Hke4?*r<{Dmaa7+|St57c zx^?o;R`x9wy7=u`9oyYI$!?{^E z&aj>TeSY=LNBfra%$oPLIQK(!(%YUMn>+Jo{F=7C{3-kWy=g){-)lb{m|%0l#{S&L zs@fy>?RIB96}^7t+O*j+9iOYx4+U?a0fb7XK}pwm)rV zH_KF2{yf)op`M+^oYiy7Kk3Bs#M>}198k?}wvB%GFupZu|HrgXYjaL&to?D)Z7;i8 zm%xLG*A6;LiUrx;-TeDPPUf1m(wfWHYT0gGw#Z7GS2*_Ctoh+42KpD?y4t+TduQIf zOMQ#*yS6;Xg>e#}6lU4R`F!$~+-)%_N$rHa|8s$}etr|yPkuZ3|IbreX6n{vT83t3 znwpBo=HFQ4svF7uHL>v{OMU+uxhbvAv-5tw_-rla=Xc)z|K7xN62ki`KtX%GZTEaX z={q~Q^lO-_?%Btz;Xmx^?xK3le^JwKjgyy^m&dnk5lntN?V5r0?bpd~qApxEzxyO# zI_&oNIr9#Q z^D{7fh}-fpcIS)>YX2AUUJKc{IDDp`^ksefGoh?M+*(>(rt-g-_b%SVJ?hT(52c$- z4lN5hdi;vk|KI*6bbdyvA3UbD_Iv6(zB>OcYbS18-u^Op{-k5anp!sWWSVTZZkL#2 zRkx8V|G-_%ME9yIa~tc^-b%~7t9iXp*wC_5K+L;CVYT^@%__l?bS!vqpjx% z$HsqI%^!2>om}s>253?b}}%g(M2RFS#BbYP#K5_p+r9Td&*lOW#^QCCjC5 zS+!|Xam_@x)OQzO#7wL+*icrxW3SLfi!J)s4uHe(-PXoW-xso+&ab(9{_0gzOG`;e zaRXi|gJszasK2n~V`p(u(V?eBGkr`HX7bkW-(i3E`l=AEhj#1?7misotFFq*&6Spv zY~3G!@BWP&72e+3txk=;>(YdlZ7vHn|u?Ty>IWHE&IH0 z|E^PfbNRc5_3hi%AyyraKi#l@{?)VgtKb$!275p0sB2!6yPwu7D=&^#+m^bx{r%4r z7s-Pyi}wBP_mp-%ETfmcQ~Bes_y1~sAN;%e|BuPmJ9Epct<)D+bsiJzJnE#_zw`Wq z4=ov~8$Wb--d}9Y$CBZ+EP!+6)y$1c_7rvoop|_3C1|BfPgq(JA3v+o#)jjz4?gtm z$g%1@_wXa1WyWP&ji>i5Urg*M?7JtaQKLSq*$&kFJf7jKYr$c*NrnIQqbsX^yjgp+ zb6?-SML#}#s(aO$X06T9d{9ld=*XG3J0C{IW|Z7I_2$Npl6wDWGtTaVzpkH)OK}eR zVD`F;`$Xcu(8HTcId>d+^)Pu!$Jagu9kqsl!U!v-cMl%)X~!h*d$A(h ztM^0yxixPyPOZwCox(k%mi4FU+RN2B-|Vh<-^;XRHr3v?Ws8W2$cxw6kov3U{?FJABb@zm2Av)S4gywq~`|GA^Nr6eiW^8WWu<9#bEE}fn= zC2o$K>PgIwgErRc-0*`X>S6Tjp3#EPdWpTD9l<)#&rWClC7P z7^v?3>cTx4H2u4KZT{sC8SB0qpT2PFR86&ZPGE8Iq+a!T9y|qA&m?ZHpW<)z_wb4Y zzb}f}$%_;Z9@DM<{2@Rmy?p7>-?MH<{%&F2DG~egkj?Iu+xHq={J7Ax@5T2g3o9ey ziW8?SoouaXt-U*YNBfg6Hx6iR*kX3_<+IrQpn3gMclxK5EjT%SyU*WM)#B<}fAe-f zzhGWHH)pMImiUesvxvucn_h`^cXuB=c+f-iMfioAH#-j|R8?2ce6If(lvdm`4=-j^ zjgdP2?boYQb0=IW*<)WRDEeTQCjagi;=5wpwHdd+*`v&9=-Qo;w8g4lMD%gLcl7kv z+tgysPs?6^JIyq<%jn(QIcpN;3cm#rYs-(yq?31N89Vy! zc>CUdcVcGhw-0@qdLsYNXlBhhHu>}uuY-A+NiHGxe!NP!WxRaNVdJ+^wR@iSria~| zapziSXUwUa(!3kLcy#f9J;>Jo-rK?;>9uWxmW+Pfo{pX#9&YZ#k3Swhe7L>6{la!e z2K#*zwN8A9{3CISrOqsFs-Td}OYu2d4>@yB`{T1l$}B+bMp?ABd{Tgbo!|5qyVb6p zw^lOKu&L+PbTE6G@#5O1&QH7N87}v}Tj-iI22Dx-;phxM_7q-}zXh6-irNlx!D$sj@TGm%8`x^N&Z1&(_RY9-eagkjt)@ zw&yjcr6?S~f92HW)9M!cOXn50aGChkpSx^d`)$G$!y<)8PN9$g&P>kD)m2kV%gErc zbiVJ*cZ;Dxelc(C^dCiwH$P=gPM)0YR<<-ZcVo+)smfEN{I}keNG*9g@nCq{#J&1` zWycJbZSU>n9c>{y5j_2YmJ^v{099Sx;_5c zb7S6k7VfXR{^J?%@vOc8o8X^M-sL8DyO*D7+9h`VSCCEKT|+}dLted^*1iNqxtrB-$kExZ4ckwyr=2#VJD82cV%Wy*m&bbe$n&OxBK7OZ#egA&g@mk?*6&G zwfg(JqgT$KF-?*F_c%XqC2Qo2^ze1}{y&;kdov|`PJD#pZ>HI&(~2#mn_bh&&q|s1 zSNLe$yRW>$TW0V5_7Fzq0>%gGSB?JLWdN43V37FDeY(b&4HRzuQ+YwlWq z_UcoQFKCvuIjoU?x;o|31;dp(a#?S!rg<;Xdi#=n74Kh$m{;FEy}5GZ#EBnQF3COb z*z14u&Yf$GY}YS+zsa6+>e;6sFHXF8@#K_R@7Y_?b7Z7tWn(KG6OF!yJy(AoRxVk6 zJLC7toxEbz_0`$g(|5HWYb=WR-Mi83*pHxBiY5VnWo(~(xW~q@M>=_zI)g8Jj;qh9N#S&8qDrVW^Jvik=ZM4XJ%)|SigUd{Sw|U zmm9zpJE&a%8%G4K?&1XrZ29Q9=xSE^`GrjTEiP0$M@L5|ZIpOd%)pSl$5@1mRjPJ% zh*s(WeZDVGUpYTHRLciy1+3(qGTU+S#SRyZ&1@e|GuAGAk;%|1-N?9uf#Fug_gfD6 z`5diI6C;$SdXzjpYcBYDlYVDIKlhi`Mf&0Y@7dpZ&QQ>KYeLUg6O(U#-lkc%zU}+* znBU=(m$cGKCuQLU3`$9@Zkm%hzPk2%EDBhq;;thWzb;Jk-U1QOvbOymxf`BK1sBcQ zd8>YA?XK-s#v6-Ph~;i6U;BrFbxzJzwMeO%5|O*bL$3cw3^K25=T4qHJLl=~>bGBR zImkX{zwNCo%R86(Zm+B|cXUqdUnixhpCh>!GdtUUExmiVR{68el&_+zWI+>~EL#q0 z^KAchwmvR*ndjHbNz?THep>ipLtjrR*Q$j9*N-hXV^wPXbw^v>XsMg{+OTsAE`FP_ z=kfIFi&|w$?`O3=?G0MFWou{DntHvddj(#;k&8A{y*4N7sm&IDGybJ7ErPX8o%9}i zEU~e?dSXe}u~C}H4B@zsOaq5lbp9o>+Z>m7k}*xKWX8e&v!2A=G*_DXYa2meZDO7 zpU0c+|9+2OZ+kXhK39Rw(#q=A?p)g*V_VygfCx?cS8V^kH4V2pclpz? zsqUP2x?|riZ=1YZEaS0nrC`g8h7C$>3D&b`T-bQ7GElnot+BEV-}fKKV%6s-s7LRf zr8-wTrP?_3=va?!Hv9u{E z)2I0slxO?AvzWHMtn53V&KiTbZ?SJw+mCHK`=-zIsqo^Y(pwMmm#`iEw&tVQ>%+bC z1f#62f3qFiK7E$qzn!y{-nw7e{;^19YKYq9aP^iwic;~U};PDF{r3-txy-$P)MO7z8UT1P$G^J6`L7L%1 zW~Q@J*w(MXU+1^Z7LN7(taQ>ZOyJn{>)UIZO111)cpYd<2{rkiKmSG09zD}(y|1M= z^o3bFUGUmB<4N1k>3bg?TG_Z!S8e{jjRrnv|GlSjEeY_9YGo2L#IO3b4 zmZxx82DN>7U3tKr?U&QjBFhYBP;Vwq`o*V({2BtSTt8SF_g#4W`0?Sx&KKA~Wu@wh zn#kzrWCIDG2h&gd`enuC%?d3RN7lns)4)N@ng)=OsB?w=SP_`-Iw4xe}|(axTIb+)q9^z%Cca z9%gm6r%GWRe+}y~*AJ6^9e}A{dwxyP?{|04Uw6uRb#--ka^LIId-rbD2vFN*wykn+ z+^fm|4RiBy?Wf-_`gS)r_qD(r3#X6yyM9Q2J^%mD%j)-cBe*xN+;}jv<=b^Fr{5dT zzWy3C|4)DB+qzw1f8V`ve6U2YYiH|^>36H%G9@)%H*w1T9yI?$f2M5xF0o(l-Z(n6 zxOlX3*Gk*MT3k3Rjtl;}yWRfZkGJoq zNiF={f9&|>+dnRb|9t%M#gB`BZzjc0otC9@XYsyypG6iP)L+KYDX_+|C24-xn+5fM zzTK2^Rm&-`Nxv?|D3Y!_->s|f5e-Wx6GMdb*{T| z{TElUpHbagasyiG!f#Gk&Ohzz;mx1&16m$@Q8Kw1c;;W)z2DznX8r5j`-H=_^jQ1% z*=K*eDzK4$_r*2UtmoAA&Gyv`%5BvBwS&&S?0*KjJx z60G`AvMK(}PxG8#H^1b+-|oNq|B|;c&t5j4vj3SrKQ^i*s!em^()avreJ#tl#ryqd zO|dVU@MYceo608Nm(JUh_gT&}{@kOZYyJv;d0#Be9WJNAtKZh6CDs>E^Znypp5Jxt zc{fh&a};d4+!atQy-C(Bv++xQ@x6ID@wfK-L~eVqv+Kg&TZM}%9;GscKwrsm%p6LTK;&G0nd-R_I#+Z{TII6@4P%G zMD6qJ&su*^fBEycHltQSWuNfd zTmN(_ZLzjBOcd*X@;WnNQ`Z-l6?^`C*?e;G@0Wjnw?8(?k9}SFjN@$W%H3tBeqPwD z>-1OupV?!ZT&@#+K*4q zoy*(*_wdanQBM|C{82c0-h745{`qbz{v^Jf^7ilC9G;NMq~BGyj$G5LNpgCe`etrl zwrq2<}$D|gk^MAoN0@8mpwytt3kRD9mrEfxXuZMU2e z7M``a_4EGw2c@@d0;Rk~`;!kb3EsLXt9se+%)iS13!b9SXH*_%*}sNMsl-pIP z!b=VrFG^-|bP9A#;HZ3WA=uO{yU@D){l4G#sH5HPc4W8$VSt?$GzC$6RLU-$I=)qj4RoczD~ z>8q`(?iue3Z0zp5u21Q#3ak6He)if#&D;04PHGL?H)SfroXQrvzi&3n*Zp|7|KIEV zx80pK23ya&JL#fQwUuDhi&V>SK_TOWQo*i^{*@A6{(iq7U;p{Nm2$|No=XZ(WsQW4 zgqR{soCIgv|9k2G`}_Ote9g~XR&3Ot$2&Po-p}rwnBSzrw-ZZpr>0*s`>LSH7I)ug zn!V2@!-(R)TX}D2i|O&+@|qp{JLL8?vw3lH9MhCTZvWeRs_a_)u9JsuKK|YQIOWP8 zr>%#+?y=bVHuL_ZzUzO|UcbH{dm#U&uM7BtvReQ&C26@*wUyo-!{MWx^;d3{B>J4ozIo5K6@?xR>P-5 z8y(ipjh(-0&Dw2eRNALXo>=!b|Ngm}jXzR^wg>+5$bI;R`|J8Wv!1`|$#^|&%f4q@ z(@n2m{x+Ad()SN9i(piS)w^?ZcN_lt`S-oNNxNgWtA_lhkpFM*TjgoEF)xjce(DkY z|G?^EnSi?u*XloguUS=}T`iZHSAYCng-PZ)lL?W}H~lYLTlS;dIK}(a`kxctug;$Q zYx);+0hf%097h>mTwLsKzhwI@gN%zWcU*cCX(lk!Pk!sW4W|U9Dn2aR*Y@hyY-s}< zr{e8p57zB0FSqGDry=_{MYwQrirLR=!V4Ak&rQ&M*%eT!bK|SWb?pl=m+m_VEOhnv zsrcW_znSA|o8xCClet|p-z}VU`0q=ZTR%P=%sKhYC197mO~%YWE3uY)L zfdw3;`MH_ayO#+^_gQY8r#7MIvGMPhQfkFTZ`m_nT`r8(_e-v>+Me=lmVbNj()6oE z_bQeY-??0~ZhxbKQ}_HuvX=|{<^L}=70X(scc zZOp2}KAsitzAE=}W=*!8xWK~6dy<{vTPpUYs;7RjH~O=)fur;z+p;@3J(~UL%f+|N zsh6lMk`mIe;S1`b9ACk6#4h7JJ`QeuG!9}rT0viC8A#byrIgl*f_pLrU) zg)zdjWtZOl53}$8`?mG^{Q^yP20v@3!maP$%h}g`dh$PRXO(XYgT+;j+Fv)*pT4g7 z9(S+$qqbH5q)VackBk>GB?Y*#s>uBo2$9`7luh|~*P!5v&b%g2i%AHE~a|NwD zHcd-;-C4P3+KQ0(BJtX@dEUg!HC&$4x4ZaxUQb=!|KIFq6E8fNctM5d_|p$E)ANry zF8DNYVN}Z{XUC@#y~0JaIA=;vJymtd@dmRXlZClp7vqJ+?)`Nh`CAh&G#zhQra5b~ zvp>7-%Y_S%O;~EOSIvn*{K=7)@Af|ox&v}0u9S6^?UKK*?Ym~FaFS=8MymbY>F3kV z%=+|M@X}#}ugWG{HTONdTzL6lnBDhlK2e5DtNrA*>F^(TiImi`{@X76loP$L{`@hA zrOdx|PfszrVA{7*KK<2<3#;eO2(Z#svOmAh;;OZ^;lhpc_C1|DgQ1{pzq*ON)9a%) z(g!SiF1S`Q2MaP;v|}%y;l*}BL5tq0>f?+Be9!{=`mO!lW(?1box~pn@iD~Q;n4l= z#pNA&dz0BoS0zx=nQEI~W|%L?^k9mc)6x$-H@Uk78aQ+dt@Q4Gf9LJ&yh@0=fuUY%;>Sp~3w%-$e9Vq7e5-<8$Bt zP5xb}`)PM3f7sJ z`(EGLKF!|zZo{wghwOp^3#ZPRG2!j^R6JPU0 zY}~dpesAQab*xeEWu_l<`pT=3ANO@PgTO-3xw7Un+ofHb`EK#|>c}j7<+tZ~g!lhj z&&<*;a!h55(_QWLzJJrfn57qjSUN6v`CpV%-FjGe^T~DZIG#?<&%IpeS(c+||9auZ zL)%QvuV4FnRr+$_Vdap2MY)2vyqBx0A3yf$eyhBTLrd7#YNw^jhRx5`tZIM#T3zkT zIjM{}F9U2(*r(?nkDaTbKY8v9g958Ole?Qvc5=sR`){wW`QiH)EGtQn?CK<`&&W`f>E(8e3OdQCpCXk z{@Qw3byGm^q?9T3+cwthvOX^_srt3H%0?(Ybz4ei>)QL78*6Ikp5Hh3^7}1cjwnVm zDL8re1^hgm;~rDJByi2ChLtwwUQ{kg-m)b;I`Q|9HCD4;{?|IZ-T3#LHwWM8G72ob z+#OK)=2G2>lwJGJ+1-xWXDV@XP1I|nvkYJK?d_d%Z(FK6$%0H>ZYLYg>+o{t$%p^W z{>eD?Zf(x`jneIC23;eq@LH83onaK+qOCRXV#)++)q!RxqI$e zxz%5%$A|NGZDVn0nI^ulHAS6uO|j3$=|@i zSfHqMrA~Lp=4gI~*Fp;~r8a)b-a2_Ys?ay;yrr+_@ z-yd)I`mfiV{&x4}*wTCRG!CTH+upo>{@J@(+&*%i*TbHu)F-|4PCu({U;gdsD)Fte zV-Hu)n*2$5D`UV{r8`qix961o`x5xbsEXlvqSZQ+kdy2@PdV3lG2co%ki}iHB{6L8 zt>S3?)mE`;yT80DS#ovT%C+$uvp0VZocM=fPCLiccBxqRNI!{%NiA1)t$Mj7`ln3G zFOB6fv;GweJahNxVmNozaA8f~>Z(~W#rgN)_&%<2#DnSCR0lhsFNH75lpr4|lGFjjHu5KsVP1*ZlMMW+Ui zMyQO)y^6Cz96 zo|V=7IK(aeesB5VIx#_}Fwun?7mgi!y7ujK(CA0jw)HVLca(Ux3n(Up3U(O=>~vVa zztHxK)s|Y-W6s_;qTkB)9dEC`S^svoD~G_sHx>atyG2j_{PlYM|4H}w`1rcmlN(=O zU%%ab>5h7vx*gMNvr0-SHTldO7~ zZvXrCvtoDmpD)r&ru4XZ))-XQRH>gTnREZrQ7yq)wMBpS|F^ESvGkI+k$O6RZs^)Q za=&W@nOrpF8GNfYfB&GdOUQFpPfk+Jl;evHydEvIyj*y+B~B;ij^y&yx@zBf=DBcG zoKq4J&Mh=6GkYucIp2^w+~@e;hy3*~=Jt87o^Vz5pS9#27u`+&ueliS_Me$$4?11Vig0hs(mkQT2tUvdelV<-FMZ-Jo*E^!|3OQd!PLzKGcBsClNWy$d7E*r!lu_(hF7Zpv$FHj3ZK3Y zTii6Hubr{X*0Z`kZt5HM7KCeCm~cUv6v8@00m&EyuAX zayLWn1YR-{nxz+DWw`M1mAALH%=^2KgYo>$+uvN;lWan2Z_itMT0ifY#ERt(3Qh`6 z-W>rSyH^)7InCh^f7EAg;M&q~xmy5N;$u-_Q9>k5a7qQG*exfjkLTv)9p4(}9CPek z^>IZfP;>BZ`Qz9v@hkRqe=Dnz$X_d)zaiJYn}cz6SAd9yo|MRRzN|QZ4ZX7Mg)b-Z zDLNfkr6iKP;2P6Y*<1UB9xqJ4@43@%I|rlEm0}r@-|5BZGTG{DO%t|bC4s#rpWIycZQ}OwxvWcBc zYV+(+_Z8D*1cek6Wc@DQ>$uPUN-JQ_-n|<)8ot=mFz@A#{=8!;H=k@c>vXyB^2f_h zCeGt{+I{w#f|C-<8sC;j<$>mAdPmNzsKPBD5um=StLqJD4GdnL$m++nLB|dv?`mS4#R;Pb| zzHVo+cUP`kOM_`wK&n~Zq?Q+MyT0%Lc~PUYdb7yX;KGZNLg%-}p=-#|>x|!_s?dn-3vbiCB(yEKoY<97QK6TGJpB3x>f7b8q zEP_nuRGbQr9o!%+d{3rqhbo(v;e!q~-W8`c-BJ2DCGzWfs+hK7Ayo3mXcYx z{a)4Y>HBNmioblj^?F?O@3-5bl~~vIQ*}R|PQPFC`E2~(SK;}~x9z*}$%axwSDDIFFjbqN|_q6}|@%VOp?br7= z>;3obkG@tVp!guhMWc`TsLq}B)%k|{*Gp?&T=uvB-T(i``7?W~HO;N@19?r_vo;&+X)YkmIiL|g%*PA()ILq@BMtvdSk@rosqFo zr>AtDzosNo-gREA-+k|<*6jWkiA>Y^sr&Vv4!HIQh_%f#b+=!nx|L({Z)d?pP9YH_rGQZW2@kRczwv3CAmMoG<7q`+TIONafMyvxWiLi=L@1uL#@b2dew#&F45; zt#l-R$+j{Lxjj{1qt4G(o8s?$;#{BhIn561vm8_XU&_fXd=z=p>g{(|j*5HtwyC#S zfBfxG_~ZH9FWX*ie|yp|dIP(-qC-pC@%AorgI?>k#~b}tNT`3EAKA{qD6Z#(-ZEiP zV(Ab7;SSJnD5%W>!ku6tC6*p_J}UZNR$P2{_x4Gf+g)24rn+m~bN&5vdVF8^ z^`o=X_s_i-_E+x~sP?IBd{pHmIPcnP&ZEnd7AG<2cma;UXD@f$IgIdHVC&^vda@AC$6Ti4xNHMSP7clBst5S%5vu)@FPNAHzud`FwZ z*T-eoUT(Rz=*qJfE+AQ{FWYPl?{wa0?>cP%eu;Vh?Pd}W1>T`IVjHsr|rhp_t7%9e9cV1wZugP#f`RWM&430*Qt-EV~Z{yhbisNYi z%>9!UoOH{|^WVOEcPvlo$QDkWz;DG{H*fy^@%a27;5O_x!TB{p|NZvL3af1>`M9|B z^|iGelR(-qN4s!zg%q6P_;8O*qxFM9Apsf4#yOUauH!^DuHiQf2Q_eX3P48YFr;t@ opn*Uq$c&Hq5o;71eL`1}lS+i#SyUA-bckbNQ-la=b|L9-1XC3fBb>95> z?dm4q9_G~4)D#vLCL}PlIu+WjFqG(-I8o3p=~$g2n`>-zboG0O#+(DUsQ9rk@5F7XJ9~HT+}Yd9yJGi!<$wpQhY}1{ z@aG0hnKy6VYtL0d)Wr7aWdJMKGE9`ol$=!i8m zxh;MexY2&j$3~`iyk13rxD7S5b_joHdM~bV!KlJGKySkDWW8rEen>Q{&z)3o+}-^> z+mzMo|Ni_wyZ&j-ea||PfP3HC4u~dms2EB1?|OM_{mypt)L(839E2YoW@K$W_MqT~ zu!)EeZ|8;gU7{jf)kQW}!-RtG)V3U6a9>X2PP@0gUbg}J=d*7T4}^b-iFEs2d3pNY zTNB@>QK+;6p71cnf`eFvX^pkk&zFRR=u5OT4OU&>r?sLM@Ms+rm|*USS|c@>QTK{ z`gYUH*REMJ&wfc^MoaI}pHJ5^i;1VaxO(*H*4QrxYu25*zvL2|S>f_C+u6Cen$>l_ ziOta7KVgDE^}381_c!|~37xpJS}plv%=t#uNe|xzbVR%pH<|WZc<#xE53>IHUJ;Gg zn;&`jG_&hm!xtf1IM`y<^muWe3F6=a);irB`GEl-uWw~MG zoVQHj@9#_#Wb% z)m?bv#oA?$T5jB!vus~ccfP&&coR#c$FA|6d_3JsW{b($w;}SJGq#+Sf3#4@ZQ;k1TY>Ixf_d+6bhhbjk=FQn z{er=UKtX|WueDvpqIFt}vR}XaYINbz!Sw#4ypvxY^XG{8a-3y~FjwaOGr`_r`gz?A zC%=jaS7-=Mjeff1-}QP9&6VC<94lK=RcC2g9h>z1c+Zwzr%#*Cvi|?#5fvQxqH#%R zQPzY-efgTLhb<#>KA0L#ss4D?yhyF{pxmUsoA24CTs~^F^8J%j$ecHOM$99QPSbgRLaytbx*im+-gZFk=*^XxEvmmyYp42L^s`br zV%yBk9Q)>s(B|ace_w7E`|8fDj<@rBb4&G+UxlbZ=5|#thqKuk{O-qeew_aMRlZ4U z)}`}ur!0=OL~+=eYD-4lvX$x&z1*uYclpK@^3hkD4^2GTUD~+GNy}8b;P8xbbY^Zd3$<)+P-&vLX6#=_rD)aEG@Zu>Ep}j zZGiz1HhCL0!+tFE`}X#LW}6b|*rwykp-Q7_naRJL4wKwUKsO{r_L@OEo*MJb{hNX^-=z zbiS1z8Y1tA>DJcnSzT7q{Qm%F_TG(ga&mGHc@GD_Wi_04Z{JVd;(`YucfIrHAGs*2 z^nS%_w&!nOzuvvE_t0CWMd87Fw#IRsnms$qzDn&?tf6VZE2X~?eo+xu)by`3 z8xs>17I1n~ZR&1Im#6cavUUaCuT7MVk$RGmH^am-Xu0Aq55Ma!@i#T9ljb*Ae%Cnr zkblY@_Ogp2OzU({2fnzNY%46RT>btDi+E_HtYxP-pX=hMd7sMVyxT?i=L*=bTGf92 zuX|#L%ay3?(!{vDx=iQyzYOQK+?CGJI(hzl{+pTc@v~)S$2!U8^!NAbzg<{(Tv}Gt z_2)FHg-tO0(e7oV$YnjI%k4w)LZQWVPn%WyH-EJY2w|@JY4I5Ua zv*+=(ZZN$W9TIWFa;x(~f$hg;$FZ?zch;#sow#Ms4ja>*MNSH9`nQWbeJ&bay5^1S zXIri>ef?(QY;Ni0d=Kx4a@gNP3YA72O%k?8#$x8OteLhZaKwhXMts_JSu zx^?TGK7HECpL;+$D=X_4qeJEXW4y<{ zd^xa#^MagMogy2HR6^$gZvMk}Z@)iV7aSUD`a@76M!n{cc15!E=XB!}vUe;>YJNQZ zx%39Z`Fq)7`!jQMpVpXsOAP$f7G{vNKQlA)=Z1T78{=CP9UOTWxso^n1e_b1B$z~w z2rN+erp<0buw=U~>rd^-_E!-n{Uyg?`HlU;d=9WEbmwy+Ar;MQ=Ub`Acr^R!)cBgbLQma=eN&a z5u#-w`Y$kMn!wiudo6U8OjJ}%D*p()6DnS}G&AGdgwjsdB2Mown+lk(9;y2`eepVv zT3x2)`{%TnH+xK0x0hzBT{zk~ zSoVy);+(Gk-W#`XsQss6fAQr%pKAZ(36YEDC4Kp( z8`m|xK>gMS*BJhHK|;?Gc6GP7KYdzrGhH%nZ_DST5Bp`$@|v2T4_bHM^_#ExS6c>F zhC)e=^Yd(Xm%o>re6r=?8}0=K-i0?-{J4Ic?Vq6FzG<&!iIsjn(mL(g!JWx^8Yft6 z8qcI`&M>&ucX;y43G+hb7Ooe$?WZ=WF~99q*M%*cW!aFHTY(Dma5v^hU0=K zL)2>013EXjE}#qf5H%GRf%O$4Z$@TWVik?0&xmp_7t(yi*J%Qj{J}Iad99 zCJ)>8iX5@eJUcgpKGDsYZOU**_J^PKjLg@0@*3V3CiXvJYV~CF?GILaoz;3;Au)}4 zD@)RD&X`HO=GyPFEvLLxdKKiOFexLaPIKlSnVtVDk1k`}-0rjV^LfXIaep;M*cvWf zVR2~GZnt(?GFQ39tr>vyZ*_F+G`h;i8lr5r`w>mlP#6{}!WG^};e(ZmKW|~!C4?n^Is$utt@;v+PhM4I$Htnfo_gqC2Ojo+rxtoVAbIl=-hdVc1NG{#o z(=31YQ>AbKgOY>I=B-;>P5Aiu_WymG|8w$*^~|aJZ~oX>Yu)ww=eH>1p3>ObHM+u+ zv;TgT|99Djxi>o2?!knx+l&6Zo%jE1cJq#Rw^)<|S1&p|*-X3e{h><++Wz0yZR1+& z-tQZ;w`Kn?{pLB5`{u{#e4BWsXqH`*WXhSof|u&Q8rehG+BZfE$tEcLdm7x_@G0T+ z{n~7+o}+Kpu1TM9>J@v4fBEqY4+);fKl_`Fr)K^Qu+7Tf9{F@#*+I6s&9i*CODF&T zrd{~_*QE+c&*x2lH=cQZO7FnR(k&)uWbeQ1|Nbz{_GMI@>ReyPwnJQOWp0=1g~QIA z@19z}l zvD;xjD>?0{Xz2852}w~mqMz4=s}-bg_kF!F@#PbbC2NnKel_#yw~SiN{Zqg5id|H0hr!&U!T26hv`jMDqcDCeH(=CP<=g5g?JbV!n z5c)oS@lz40G*!*VGFQVUWv9>J-=1^R`;3;I>XCEDw;I|0IXd0z;Y-P#KcCumYs|A= zZvJytr1kX=b2H-SY$}XV{&9=VKG*hhhXH$~;pc{*TTfm(B$8bnz5UQF9q#217P+1Y z+qib&MTKuX8W-FqR@^+2cy5=6#kD@)BbBdaSG88WI`P`_>a^4>3#Z9R`&|z{NsV>h zwQ}hM@VkLwNt8RA6Z8S=fvoG7hlTl;mY>vncunU*pk$zPpA6j zJg#Wp+#V#$V*8-Tqx0D&*QsA`Z8{VscQMkk(sXaIu4ddR{oRZ`7u^=^d}>D4ROPL7Lwtf!VFaV2KfqT>5D`)uz9M%ZoI zrxtPi+_KH94?Sot3Oaoxq24w4`^B3jkA7WHKQ9ubb=&<}x%8o>SDQ{H=A~`g)Z&{{ zJ8}CZZr0b+o;_MKtB)a<@nORbmssQNJ)MW2`Y@+{wL2pj(018Zu4+p0ip9_Tb+oIc z&h$>HEtY!xb;;7Rr@4fe_D*8Am>qh4>fNfeU$I}F#ytAWZ1N;^t@fu!Pv3Aw-8yt8 z^=h;GGPc#xyQ+woZTb&ryGvAcS?$@ew7-_j)Q!3}p_itOYX~v~UrNpJCr>{BOziGX0O;z3+ z`^6;*&9z3eXGHGxeEx=C^rqCym$QF|Upw3XZRvF0cLf%^Z`PFsD9t{xXy(U-O;e^N z-#a4zPjxcCI=51{qF~w%Ba3yd2NM0S9G%iHyNUn!BW~;I;#FQ+-N)E|e=@e}c#!H{ zTk`PUi(jRA56-3W8*=8p)T^qRCg-@X_Ck$?)RzsWEh-O>?p>{1x{QtOS5=iE@13fw zeO9+l96E5%c+tkniKkSqsNRU0saf~+c(v>6oHH(s?Q^G|*t)l8%d{VNsrjwAarBykb@tTd?)+MZ#&ZT0d=pkN)i8u5NSjI6Fe`nL zex#9eEn7+C&HMCicLm-g-522gH}7)HTb_CHrPT#LZT^0r`l9OnwA!~<#4GPkIQ~6O zs=K!O&F@d)R#kRq4S{@ji27Si9o_j>#hIseLc z@$vi#8=U4z8EJG#pmhMe*}0rrUEZ)WvOfB#QH@>mTGl`ib}2 zoCn9#9qs1ZJ5)@Y^;c`fXXi)O>t%#yAAWe}K!mmOmcI`Y&hmSnbKCRaHv^ZAmIIr? zO+D}Tg-7c(L~b-}EoIPK-}|rlzj@ z@JaQY&iCSNE@5{z3UfQZc=Jg>_5P`y*N@-To9HmJd1u_Lc_$qhY+^M{w|!iDj%UaB z7qfTR<;7j?JHKy=x%}Pe&XBFSw-&|f-r#*4_ox0hbEREc=8W0wf6iUC;d{lDifTlUO!7UTC~rO*Ga|LT>{3n+?Vl{sy$A^kS--MVv%FHc+W z6>q+L`tDuhO*xZK$SO9qpFWm<%yNb6voeuiY%YBAiBW(5J>R$?JjAqbpYRerrFDyL zZnb>>*J*XpTcLm*=V#jm&VTacNsJMnj&+H9;Af_`sy!F3-LnmnTlDFs{JUtEsbBg_ zmM&NSRlP%{?0vxr=~UCX)+VRi;@!i)2khzm5N|JDd?ru*!UV;R?+*eG)H^U`95{CT z`2XMg|6BJ+@Ar#I@=dYdmwo39v)8`$Aq91@8z#+|&{`f-_wT@sl4a{AF6`fa>9M}- z<~E7vyaKPPR$pqiKecPy!Eb4`3${#656hZTUlHOpYyIMlmB)|m*}tOh>o3cIiuRl> z>Z?z`U{`JS*pzww&xXYfb^o~PjE-M8qPt4xrtb^urB7zv3yR-rZ)@J=`7vP1*V(hz zh=p%>t2Fn@ZVBtk=PAD=>}nGyU9)ptsO<1_Wh0aH0}s3Ze~L{_O&J*(1qCNcOXjVa z$uyDunkLxRm(-C6u zbi$n4qhgf{&)j>NddtIo=j1S%Ez7Ht*NQq-Th4jRQG4^!D=FRN^^RKmj1MSUtXimT zfA7{pwv|=)lCLJ`=JJM|sQ)>=e&y=by9G65oceFp&OGFL<5)|!&8L&^qVC#TNono* z>~g~R)zVdS!XERLzdHL%Zr-s62V1sGev~w8Mwoa`s-$(JUEUYLg0qTm&i0+r%kv4l z`8sR++Ot17UR`?|qxQ|<_vE)KXNo)L9CnzR?&+Hw_sFW!xP9(Kt*E@rX5X7)UHS`q zFHL>0K+yWg!Y{K=uQ1&fs_hk7F8e-Zw)SlIYulH%O3f0VtK?8w@6b5!!~TD-_uEx` zc#w2(|7nMZd*7=hJoU7jHf?hCx}517_ivx{WRa@gVwLdr-Kz6Chl2qCAjwcpJb+$mL5+VH_Vvwq4xEPReQgBN>5y`y7p0#6K~!3pUIgI z*{%NhvM5>I40tth&x{MgD-#l`{KHn>*{T0DzG5 zCH*^BO0Y|7pFW+P@y`0R;-YV1;o;vd{t4Cb+kS2O^zdu`34X%*ri{fkhaP7Xs@E>; zcQ_*MGXJM+_>3v8DgqfkY>Zp?Z{*sv?DXv8Ke}x)jx4@9)s$zh{a5bYdw1@uZfR<5 zT(G6$%ei0Q!XhU;WZwMb>epN)ecs7Ew{FDPTO12qn0RR6{5QF4-Yk0QA^vkY?~cav z4XJrQPrJQ;_wLZAHgRsx&&v7XeY;v~N=uD2fAcbK*M7cpqEofz*R2tEe?Ia0b}At_ z^Zzr~xbUZHmiC8P?#3^6Z(MQfpzpDGd(o;B@(+(HGzZw52Sl7$ppaxQpwXe&#=vUG zqS1jE%lZ9e_whI8Qbq3t7R>Ptl9!if_igT6%~su@#MyXj`;k+-+2il2F8Q@?qnt|B z?`sZ>J~DE0N%6)P_AqE(JiSSLdj7lK^X=T4Y-x!JN&N>ZqQhBUYPz3(y!o@up^N;s zEshp5=E>Qr?3?@i@v+6<(~gUW`Ac>j|8vlxGS=?Pp$5xpyQ(v_LZ8pRyjttcbC2U} zO0v__7w?`vocp=F`pUz7UMGz{n?61NI@$TUTU$c%@`K(N57qVln#J?ts^P-Zi><#L z)vAp>`Rna%mgsfQN;HpVb?$6(KETM!>AaseVBN{~xErdo7{9aWi{7*`?zLq5C6#?L zFu*2J;;`c49k1`aU$Em=-;KqG=YE~~HO<`d@0M?!Kc5~BzNfrs?wf-*9w|01pX6y7 zH)HP1`!-B6h8^ykFJE4LyW*Q=+qbBuk{sRH?a$2l{eKu(M9iEyFIuhXi#)5p#jTmM zqwhEe|42Sn-Cex7IJ9%P_GjmU`j%E+gJEr+gVC!zP?H!8(F23gGp51fZX! zabssvv~8`ht7H46nJ2mQRSOjb6+ep4I>#OHD`)QS&DKq)ZprcHUcDw2-_@bLU!Y>1 zRn?}O4+{N59K)Z?+tIgm_taa#j%OcDbiU-ad;Z3meLsTdu26jAvv0+ZVAsP36!c?{ zpO~_5mW8l&|D;(=-lCqRQ^osU{#|}1es}cGjSdgFABVBeRP}pT^21VPt6uW5PbzYD z1_q|pAJ5hvo-rwb+j07-TE6sy|29UvQhC1PVyL0cUfY9_$8Dy19sU)$%3`_t%i6CO zmX?T>7i|iYSN(J})>itrc>Mo^8q?P+pR&y077I7OyKGX?+_$llvmU>lXA`hxOK9b% znIQ?8)0VEO6nU0#@q~-vzMa+{k$Q!bPJGpo?v3(Ny2{6)8Qrg}WzJ*ugkLa&6J96;UN-4gc@=M0&Uq;T0Ue{MRDX7S+w^Xz)A?l{J zg`MHf4Xuwidz8)o%Ug43!6uFETgy037F^z!EO^_*#LU!u^R=m&uLNfJ+uht$ysc-Y zZdvWJ`%Tu9r)=4BN#agwsk(Lcu`mADD?Qw|9O``X<NZQZ`+08oW|2J=$E|)@Yda$;q$MIr?y` zTUS<`-8-Wyx2Lweg^V9F|G9k^ky&-ydyVQ6*`%V&m*U0D) za&^X9#i(Gf%ZmE;_KM3_*?FG6U@BU_V^RL1c{hw_ztz%S^K90XFw1M984L99?sUxW zG?~o#Qciz+?W5SUH`mnZW#9L=Tv=#P@>$cbd;1hKPVd6Nz29|8Dh$4DZ2T=Yd2?Xa zx|gyx0V0d_@}=b@A55L|a8X74O?&q953<{p94e2;osZ1on{5&l9cRDi@cRxKf77<# z6~BL|ebdQ1|MjZR`^Lw|N}j6x_+9Z^^~$@?x8|Pqt+cb-_f&a-R(HNH&-ahqIxnA| zdM4+0mGwtOHT&^zxf6a&v^{mu$1!F@f4_g&U6$<*SN^`Tu+Ptb|K!)nw@2U2d&sIK zdHv?qT?{%gmx`wCUtjKU>%p6v{QT2$^L85Fy=}9zKkEH@$-6B-T}sa~vwyjKwsGCU znYKz*cE$(HZ5D6XnE3EV#_bL!1M!k3NA{9nk*XTRjf}`og12LNmGv%Mmv~9q!Q~tH~iRkNO=PL+%4O-?cnv<^xpiPKxuwb zfQJkBN8N=-Me`4yJ!UC=O|Lj^@+*ZfmKSPU^WyAO=eFFu>l7%M@yB7o9zFRgNe%Ng z)ipK0Zq1PWU44x!Wl`2@ZtJiQ+x3Mie&(!U(>M11E%fNMcJ#*lSndyRw3F}oKdwCY z<@GJCB_@;G?Us5^=Xd0N`r@REtOD;QdA=#~9<3?w%_Z%&=00X@tzP_Gdd`)rThC0I zzEf#J_0Dt8q@<)?*-6g+QJk3krDoFPb$!d+;vReW`evTLrulry{czvdVCTbYOH%R| zsVQFwj6Hn8a1GD-B>6ATrDo2Mc(t?ijokKjtDV2D<-F%-eeo)5_3f*=N$1lf63sU; z_xkkk#t2)#T(kGn^pp4WUcLJDPFGvur^^ceg3w^G|KHwCzVy`igHd2`;ybq}skF%0 z9gZ)<@>gwC>wdHP( zopRxtxf`l~tPS<@oU(C6Xa30m?|GfGs$Iip zwl)P^{4-NN`}6dD#m5)yNY1KQWPLZ(ZegCn^`kEL%@=E&^SXaw|2mDpcPmrBo~fDi zaBjO)W9-%3=GWJ^P83|Q$2dS{*AA=AGM}=hPF*DvAb-+!W!tU1d1=QAue!_R^mi?d z{cSb(ztj8g&m(SGw=GS(UcS}ZdiLHm>p8?sv(yU;<{rGXuvPh|-?X^QvA)8y5RyhVvoa)7+bAL~W@T-LPGIM8K+uY=4bIPA_%3ij&yWK>!@7zq<(HmF% zb@|4*v;TfHTlFr}^4}~r#;q?_a%l?R=G&L2GkKroosAoI%PsmibIQ);OV@fv1gH1R zig0-R&1+|SM8U;?zO^fhZP>P^r!N-0cSCk%rG3>53yH;luc&XkcK+Ngt@<7HDb>rb zzqpt0duiEEy)281o?(`S(zkOzI4vkhI1zhdOVVtUd8rw%X9vubSdcJh$Cle!Y9BP7 z^JuhO{CM*1)=q_m;+vjzZmiqX`7}2$Z5Ll8vvKdiTT*4cyi0c}y<6Ned1K$k=EHiE z_3e!NUZnncRejN(HK1UzUeO{ap-Go6UHT{bxHwjFR@0(IM(^%gaOLj3=MnB3{OVx+ zz6-K;7TwJ{;cQ$US|>VhU*Z39{Mb)%|3&=Piw)R8&_d##!yUDwI9%X0=OH zrfHav$drK7@2wXGO=F%qzaz5sc!l)pQzyM%RDIVGj@bX}+nd?Dj&I2L)$7ohYPRlJ z;Ev+02|u|EA|jg)|8{zOmec00oF~u6jlt8ee)hhhI``$-4>Pv`kK=#S4n3ZZ1Fe>d~V|le&IaXqU_poO$#4|G2MP&%aSx zetl6&`tv;&^J?F!Uwh4J;a_Wc)>JT9rRevvjdC1|9^@Hz+&{cNU-0RleE}>&8-B1d zx1LSgpXefx7^dvt$b;6M31xa3YnhQ_Cba&?(X#&a=Z*d=vMuy6`YJokuc@`qU-SAk z?|qK5-!5ai_e*x;e0B|s9NzeA@Pe2mdGp|sH%8Lxm4+?29+DVGbe6TJ#=)V&D@iV=T5z< z{K!>edf#nX`G^A>*Ks{mnzZA+u!c?VuVA+(wD3MzGINsQD!auf71J#{a0#5 zE<5kpRP(iT)eYa>-`R6`Te+XDTj8%W#rp5x|3BB(+fT0%Sy=qu|Nm0!%BL4DpJDLb zJLhJ2y4%^Q>093!PqbvV*AFPOo^(~U$!x`ye=~6*@F*o<~>4_T!b&o3? zy*zD&lIrS&f1C0XHBS0Ohx#e*s_V1X>Zq9Zol)dV!K=ytf6we#v*twb%#LYKd$(m| zR<3nWe_UUq{Cc|N!cS8(YO?3=KgE3hSntdlc0t?i4{x^4eLU4+Eqm0(w`T>VnDQ)d z*RE7La{7Q6>1#reKvbz?hN$!)`4FOmGi zrn1NRO}D_eGx2YI%*u*x**LK7eBJhavFF{BR#}?bvzgbqb%mW%W2z7IU%BQ(LC6{w zKAY5gj_)5R?Y-lI+bq2eTR(eCWt=g+@INsB`v`S)19>q^VVd~xjBz3 z>{oY7t(sWoiaq=b8U&|h@wm1uO-V^f(Oor7!-vi6ez3Pslv~W@&AS#GT~scedhFfV zZT>m;lLEpIm5Qn?O=T+1{*-(->$&8Pk4)`7W)Cj6s~*XhU$AL%GuQG9KQ4AD?6)$h zwe@n3jP$~eBR(+Xkg}<{>XF^G-DhuQ@ozbQC(YO_>+n(+m6S7w zyMp6itvYc^`(~K`8%5Le=Y`!XC!5^M{j2b8>Q3A3o5gNiH{LDdzH?KU(eK~h#nEOb zCfD^(y!qPubIh-*ZJ)iiJc|19=&Jp}&2=#WKIRyr8+LsL09b zO|W4D#{t$Q%j%OB$jHbXP-ouge{_bA+5y%-r~JdderRNx%2#tq@OKE$2Bt}>4?+YM zD4_NSI0EVv0v7E4>J{B|(q$eC(-di*KBma#vuDpfeVWQ~){%Yg>eNr`#1ABVu$$Qa zQyt&Za|GLKIN zgfa2eHYk3&%g(}m(a4fQE0 zE&6v%Ei7hCpME_26;tNAg$orOc=qo-l(5ON=deXK=WV7LA&%WJ?1uE*T;3!h|`PD_5XfGYrcK*=+>=8|K^_k_~M5| zezx=bGt1ga#GQ@jUrU{^)9$S{SF`h9qum_=1xKFNv^YGx&T{we-GG+|w#}GvVA zrAwCV`|*fdR#w)6U*^*N`}OyN9%^mM{i^a>o3+{S-8+(HEk*i$D?i`q47(SMN* zy*WO;A$;2FYgWFBxpLvvv*d|}{^>sH(HboLWvQK#>IpNn?=51h)SmJ+D&772he;CG z?Q3-NveK5MCWP8H@4cep^hSAgl|zb{)Y<}(5c7UH+h6zp|BV+E6s$8&O-*H;mR0%n z1B2$3=925r|KFd!Y;wdKujok^>i^x_R@S^R;#Y7XE2_Uq$7Ia{!GOTiE58oC88#p5jHdRHf<^FU*q#t=pvt8THyG+r~djm~>KD^Li*-`_?$s|7$ZI z8BBAw_A0wDQAlu-pdrWkH+mk%{&^W*CfqCLMy&ANQ1>V1in`62>srZ0JA97YKA&MD zzwUA5=C0T^pTG8;IKF86BHh$^U0z-qt3u9fnjrS}_-)%Z7Ke!~3S3;RyIwRfge}lM zqJQM;ql=jf-grM2o#M5$@chXXLrs@29}VZZuzpO^`YIB#Y2}n)sgkJ~B0R4+oE7FD zQZ%{YzVveDa*=l*P1Zd#oXTx0#aUWX(Q|B`5;Ko{Ws2A(1|x+6rn|`*dz+LS*9km$ zwzWZeuDs4euLU2>BA-W}`FvB0hR zd*@!Qtmahtdq)G)OvZ6-uWZDP!tv{OqyPj^WDFkL*=aJ|R=g|m}`t)CU% zDG|T))90A0&=TISj15fZm@CxHvtBXs3w5wR7ZsT9x!26rE<#*eLE+tudj>l#tS?=% z-O7~obIaD1@2-8@xAZNed}jBXiM3i)l}TyMXR^hZuTN`=dzqD=D;c|JqvejfGrwKy z6Xg-UvGuj|?ub{K?-I)M|FiTTU3>oQm+z%Md2@|CmBY&4N&XPgc{ji4#O~VM+}PyP zE>gnncW2F%l9h>73Yf_D{XCyY#`FajUr0AHF}FCF?YI*3@G3`3qOm1ck{aW#((7`|a`g+Ohz%8e&ldgKddHDaIr@@}G>DxD%RS2AOTl}BspIkI7u#M7I3->dD9@3WtQ3t3)ZO{<}6%ia=qQj zNbX-CZ}QoDkHlWC{VVuxl2_c%-!AJG7?f^}jsLT9&erwyamTa#H_n{1YUb?5Rb7{V zoSCzG%7!^xSAO~XyNz*btYu@vomabH~lbZ>2iY+po==BXHfKH!U%(cyjWhKaBY_Y09j<%ui21MJgH3z)^3GNGp!)gq=RXB+vss_4=RamK|H<(s#_Kv~ z8PAyWS><`&i$&En4>#z&-!EX3{^9m%pI@u}K4yP<#^GWYkuptwL;C^F1lEkr4-$Ud zRIUwR(J*KKBv>oLqQP;N0aAXUEa7e|>7V${kZH<$?j4R&CxjMl6qww4!gj))2|JH- z1gw*=_v3%Sa58{_YeQ<|dY0P?Ipj9&|`IlOGY<2B)>Z|wc1-Ue_%Z2 z_2D%uOG6+3Vta!Z#WRx`G&N_Oy|--ErKAm?*1eSN_+uPrfA?vwfmCUFY0=+~3e&e= zr=-s|7wu>CP?I}*X8j_MnmDz`&zc1n>T3X#uff1f3Mw_ zd-8J9mR44`EY;1=d_Mm1oEvAnY2l6VAL~tO79CDLKkIDxmj>?rC8-miY~JXi_V`cD zbpK6(s_ULz*i&3}bYX19rvF;8XV1S4TriFC(#hTd&o;+*=@Sl_7+PQC11c*vld*;7+g`Td=>OZ{G2|9>C< zuFEalciyK{hZ6%%HJhlUr0NRK;wuk7wDw4<$`|hC9AA3PuJ4#*d&o07CMfXmm6ZH- z`#L+9Eov#PyPh-g`uw`@&wqPedn>Jen04Q+s99+TZ)zWJH+5{Y`qj4Y=ie#u^5&nI z0~8%aKe;VnXwVB(to*c4SFrs5Z+WGnq9UnhQ+-R9DNQ%S7B*;+*-e$>DSIirhtmtb!_(?Zu6$>(qnAh8X&iF?W~f8 zU(F_^Eqnan@$>v>3tzqBKgY{IJ)1W#ZXxHxBmCtx2r2EtB-E~a`m^w=_z`Dy*#z-%dax)MqW6yUr^(M=%eqo zag0lKB0jCmmcQb;I-7mLx$UuEpRCpwoxP+pzD@ca6@A~>r1~&jcb=b@jI>l?5=a^DXuol$JWK6^Co$*)&4%xJM-tvjY;WS#cu|$ ziGS`gf4;Ov!25>V6??xmrs??bG`~B)Gx+(jsJ>&}r7W&X9Ck;z-88Qk$=K|v_v>Bd z1+lbXezCY&dnZhpc+t>5C&#zPfBli9zRAMQtJ3_wMIUZCy5!KQ;|qJ4-iANZTz4Zp zG9>Dl(!Q4o)rMDMLcBuMt3PxcbP>0ZJaN+Nb=J?2Eq?rFOMi>@25Sa)mTfd{Ih>>} zTWC3X+0?_=B#J#XB{^A-pZfRik4FDB(}=^&jHk{UEfsn8l|ik6<>Y*4uFn$=9(S0| zexX>`rP{LZ(6h4gnc|G1YQK&2e+zw_SXw6VdQbHk3;$F5jO;(uTzc^HW~KecAD?+< zroZv}HkCPI|EmY5+Gj4|Tb<$XfJLp@@wS-SO6OXZ$+dZNmegf-KKs0W{oj&%|8_=H zJl&dce5-NHp9^R^4C zur}ec*0*n;!gcAjV?$B5qtt|-%?eyR0*BO>xIEI}2*`8bbotn*%l-Pu)~#C)7ELkm zmwiwqo%_x0W>Q_kJo(W2!&~ou;ucP{s(a(m$TC5a<-OxqelrI72|`Xk1WvTSeO=e8 z&37`D+^!XyYnM1Qo=Hnj zZ!F`_+Pq^mo3fq4bfzVLxR*1DFkbqXl9uFl|CAx)c6(#fZ96s^8yjA@Y`v$}$tja%TrecMyRzFxlZX7bvjjqlzyu1pn~*Ksp$*6dj`BBG+^MB5)s+`j!9rt4Eja98XMRHn08sE>xw?G*(`B7cB$mj zW|25K8_kP%56-)`>CBQNmzF(!`tVef8G~2ZUyX+uD_0pT-?YNOMsI6=;a+8J8@uIc z1-o_|EG?=mHM7@L+q^?X)o^8A3443wx|8ShR%m&9dN?$E3S?focrmlsiG6|^4*9R& zzTL|r@~b2^t>~-7+~voQrp^f{t6HVa9h}S*lBwRSHy^cBEp|Co6j!hyvTJ#wa!~i;ZXj0CQ+eXP6A;&6?PVen=E_SCe?lE z@+PiNpS!bex3f!a+QjWu6m+;~)v{$(+MjQ$9g8x4-NtDhTq&u!bEjou;mg0=t8JHN z+P*F^D_yFzHm5Lkna27ex&HV!YgS~;vb2oR^7Z#Pd$O~;Q}#xhg2T^>-Nzf4XQ*-1 zu?jJ`EC}VEzah?Sg0ep6!}yZsn>td7)s=Z^X@$Qh97vqF{ra@<>t^5mH@GnI)$(K< zmYFMXWBHG%GZ!8^wP?qbH8X3CIIDY<59>Gx{o?Rd5)3?^p)}R&(4s&Ir7x=s+oeAj zpT5n`Ucn_K@UNrs(1TXDA8*4L*cI695~Tx*PJ6vt6aM+Lgn04Sh&`+q&;L49^2RSe zIn&S4_4^aGxwGx#y{34dXZxo8eR8{{+4}k#$0@E!8vA<$>@*}8Vj3*Db0Z40?7hxD zU~=$?{nW|W+V^6yf`elQ8%Kb^0tE+0_;xqQAl07@w~xvcFRAWfc=}A|pu<^)lwA!+ zSSIBO9QrRPlD$uhQM&VZeUQV$^$cte8czIetrXMW_QxmPKMgLb==TT~X0dzum(+ z3^qQlNwSV_YFeMt{8wgY!G<+akFWtuGc1wyqHgsd9ZiH3w=Ius%)21|Cv6?@_y)lpPik&$VD~IoeLwlK~ z?A&(bMNiwjdmmLr+PBP`KWRereOo&_v;F%v-w2+3_e5+)_3Hgvi|Z!UMQ4j|jMoTA zxF^0PI)45-Mu*02+-o-1Davz&p1&87>=vfpdH?M~`SPhM&&_<_t?@svv(9&0sA_oc z21mBNt5r58O=)9zD$LB)>eT3_eZ{Cj^w7>&lYn(4y)${LAr+6_0(Ouo8o z)}}vu_Vj(6I``_&gEMs(m@NuZ`@L*?hR5$G!rhAO73G7qjlz#{)3%GsAifU%rO zX|wsMcB6H5d$$BIUH@)y^F-D2`57r$H|66$GJXn)m0smdKI;Vst0KJ-Zng9R* diff --git a/doc/images/qtcreator-autotests-options.png b/doc/images/qtcreator-autotests-options.png index e880c8d61665db2a12d5f37f1bf305043793e237..3fd226939d6b757627c1af74c2d99b02a1edd9ca 100644 GIT binary patch literal 12098 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 diff --git a/doc/images/qtcreator-autotests.png b/doc/images/qtcreator-autotests.png index a7e14d5292c6b90b8f66fd35d35c7a6ac31db97c..48380a10eb8390de8d2f5eed607376df56e06096 100644 GIT binary patch literal 44003 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 z z0zR5v;=bp!V&atCmeUJClX&+c`#PD`EN{t+oa52-=TP{erAER!jE7c&vYtMhPd@#5 zSo`0pXgdX-mr+V>SAQ;Ia_BdV-B>WG#nk8dk$;-;vl?z`eJWeAEn?00sjntUUX?fe zv52fSTL=Ab>}%q7KPC}g=Cy8C)9W+#C93VSQ{*P! zzH(v7p><-l)=GBwOr@Uu*j+ezb*@LMrFWXw_ru$wW^#qQ^X{13v*AqQSzTOU4@XBmuf%n3L<{bHoA&(6! zmY1EKb5LY@>`#yf0yDd&oep;kJelLi>eV$NZ5GG94SoM?4Qf7ob^V}%x5xFoSM`nV z52w1K#^yIBwvj7WJpA$D>pJ#{p>Bd6pZ^)=#Y#o{C6o&;ayb<-W$Ud+zr1R<%`i>U z;%QpX)dbq$o`ZcoxasdRUb!m5>zr3|#Jx_ueC7CyiNd)#tipR%h3Tz6sjqhH!<0~? z>2H!Wx9IWbD~4QVd4FY{XngDORgW6<7xljW7g%^By-F)U{Lq~!X%4%CoYw@^?;A8c zc-@Lxt@Nt*s=M$1@b>oBvZHsgoa^UAOI*LdEQ*7ZPV5h~~Jui~ruWbx-3f zTTaOrX-eg1HC>l`9w}I}#p=~ev8H(KYfBX0yyta+U*Q0r2lIa1oNw{bZsxC$+Anz4 zgDY3uj4zCS#j$jTa9oIuo{#5;moX-3;j^bcxVsv(6)fYbv8|PVHZ&p5dVliOhZC)q zIapUMY4WX5`my6{=H6U|iWB1CK~isHG2dp5m&=QGGw$lYaz0}c&Li`_`kz>5JoT~m zPf_Q?2boTGsA}z=)hQCnW_i*zt!(Q`-Sj`A;o#QFRb|LPO?ZBpYsbZdK^eEyK8QJO zm44!Naoyy@m)^6UOWz{UuX-Fb9JAERYCde0ky=xKx9_iQViT1Yv2p%eFJ~KZ|JR#a z)v-w*wnR+(zdQYb{hO*M>+beDl`ePBcz!Juvax)@C#$mx72Jv|n9g%~mG^0Zn<1d1 zcRzzRi(O25m%8n(<(Ic}k6*Vb^4NZ${>IMqpZA;tVqdL`t$x+^pxMI)ee;;WoHT)N z79rQt+fpmn=>AQ9xgx_#XLg$DJvpQzV=IjyhFXun-x?a@Pq?WM3yN{tVk-@=C9Sl%7B zX1`sNa{teV@Qw$i0`=F|xlgNqxp7fx;Vb`oi{De;E&iRq_q+U`xF5P_|9qAY7MamH zZ`QvD{_o|w@6G?)n7A}(ZBC@CwK#M~7kCFc@?4pX>wV7qoHzCVmG*u3>vKPxZMU4& zot=~8=HCfpX0iWz*}mq(W8srb_7&gz?Q6H+nc`Fb>-WoCza@N4`kl)RplhDC+M8nO zi>>>9V&gr1+y95W*Y7F(%C7%sMXtNOrM3x+{H4$G%{MlSw=6sHeg1!m)^j%t_iy|5hIfIVc3=N@L`?e6gt)~&wr{U7P}d4rGb219 z;LsKRC;jKHigkZw`hTX^<%3Lvru*)9&^7D}*t!qmSfu~3b-EvKZFc_mb^7&xkC^NK zJN$n~X}iaVez`AS4_(>+|7G*_`Mvl4|9f-&&(G)U-HrJ_#aEv8pZh&6uln!z`kv|H zb?Y>`p2gUU?OP>o_suWj)2H>mwFTK((%+ZYWIkX2|KnWoNv}BNV^^<@xu1Ui-mlB@ zi(8jgy}Guy3$_BCC0-53LA%be)qLyzzZS2nczLn6{&TqP-v5_tYnR8>ysUq5^OTc~ zW89a2t$+6a|5qLV`F?%wtH1UCFYiBE^_(^T+0WM(W$F#1o?Q!>|0nwSqEFAC-gkSy z_)z~oVVz@V#JxU0-}C?1=7;gz^0KSfu2BhA2yb^k`#R1QdLt1FcpVt#=D7azdsVv? z?lnGGFQ3Zo|IFt1-snuudHSbXA2ion%KvBWz7>?&FZZVU?{}ZZGw#Q;vb%h}KF^SY zrZ<+R>zKU{^Z1I7>*anh?OnxDcRt>>=)m>QKmXUfuJ%9n{{P><*XMQav-%LOZ2xzA z|L58-&rbgN{V?gC(NDqmxA$Go`y2f!Iy&&)ocVIR@lRV9fB({dlK=6~qRfRW_}={a zusD0`)30aUL#v)u%d^LKYA=oat`+n*wFKMx{;R!ep5Q~v6&c@TS^boj=s z?kBwU^6UTi@7;WJqA;dWPkSD?DcZ1^xc@VpQqiwcsSsc+J-QhIccvgyW%)g zS8GpNkt!m-cirEeUxiwC&d{BcYgc#S=cF0DbH7wS-rjvy+;a9(-9JYrnY(-MUv8(k zeQwV0($)D=)mO`2P1(aKf7&R^eXm-!I_rAZKl^K!COry$Q$36SU$^qp{r>;Y*YB&W z*}-}zlJ)z|)8(P`lW z{^)M~?^oltf6UW;>iO)(`hS|Y{DPI1e!Bj2vE#vf$Ak4B)_cv}{`ug3wX_ves+?s0 z%>Q?K{yqyox&Q0`|9Sp@?mf#pO#e>HmxUa(ul~1Za!2?oV^rK#zC@WK3hlm3Q%xO_AEbn*H>m78xa>t4_IYM+_o!Nm^(KM2a%XwKVP z+AMo*-S|D4AU?>wnq zRz3H7srCmRkBzJB=IG_Gy|wyy{%SAlwySTKH^qPa{(kTGx~}%wsO&tE9NlNZlF2(}U8?!;GHj{%q@etX zn`8do-%}j^^wRmFOBauBelSnuV%`6qZdT3I<0it~qC0aE=GT7ke0yt0v+2IJ3tOk$ zJGS~x%d~81Wr>^T*E(qR&DU^0EWXo|>)(#Jh_$O8Otsqob#?sqNfJGL4NdoDS?m8k zf1Rrwd3r&aafRFIs&t#xeD&K^chBGS)BO5R0gd~sLw+4N^WsUW$e!sDTt_C@ZrXm^ zAWd+>+PNZoQbN59-`>@_Ds8n${NCIkw`_;y=^J*`7tLc#&6z8{Jz#D6L0ipve;-Nm zi!J$cYVD0_r`f(m9#r3&{c!cCDA9FGMQ>HS=s1*m|Mgmjb1EX|@@#orL~E441b#4N zmH+kX-SheX-%a+9E@yT)_&!&pcm2z!m1#AR`W%0CwJUb_-`vzBz9;FZ4EN%Bmz{-c+qtv+aZU&2OJr)IcYmv28oH+T zWNF5pxuP{6tloPc3_m(ick*qvw{VqI`IYH84y$8a4sO@{p}+6Xr5Fp%Ezxz}o8~>AcJ=t$ zbDdY0R~6h*d@UJ$mY0upzs~71YLz=L|VYq~0vMlDoXDhl>7yQt`pMpFO)-1=|dw zSo`D5Hk6yFo3D&DpPp*Rbtmqk(~fCDw<^S0-|L4>sM5|bJfQ8jpl8wc@TX?c2Z5j&0BT3-g8h zj-8Wa_oztUF>$NwUS+P+A`2eyha9Z`@-aVtZ`Iew{q}XAUcRqoukkq8ZTK|EAtEVa z>Mt*uzXG)nZ|80@`|O*Yvu($=$Z1D7k6!zAf?K2FhFi>-YWI{AqXn@3-+a>^180HIth7d2;8SSAOhRJ#&q3*VLG#U2K(3hE5LyKQJ&b zFi3}dIQ8}4o12@j|9ocgV0u?`ywgGbXP-~F81D7Cc5QLfcisc+{Y~r)3=B6GG==~9 zc<-;%L2JGr{D+D!-niKK)B5-^McJ9Wx)#ZJwoR=s<*Yn2i>A!n6FRv;>dAVu971Y;wPgQ0 zlX1qi8Di-M{<^Ps&5T--whGFfVdvMV0J$Wa_5ZzU^Ql((u5ZgEzHZ%T8d-Q?@dHK% z1_rZ*4;TZ$9{c^Yx!(RP`+vJqUX35JF1q)=oHKvFs&$TEon_#LMZydW2Lg{Ay#MR! z`e@JIW_E{z`4eW(ziajP;g?&pv-y@!U;b+Kf%R;Q(=`5EezWK2tA)RkOg5C|eUr82 zS(?L?ee~dgMfuv7*F4^)+9mkYw(siAsLokCkIvEE^T_?j9`DtCIjLKwn&vVmPQI|$ zHAipz+WTjnJ|6p5yC72g@($jzec>{zoA_%>zkfbo|NZ3hwYwP?Jg9znaq;okz3&&* ztaq(BAiUnBLi5i16#w;Vhpk{V-97OuI~$6h{-Su<7t_x{o+cQof;XcFCP zGw02rQyqQ0-r;^5FU|ja`%z)u<v$+Tkq7Ue<$AyS+mY*1vF>L~n&SL!dacjY z;hcWf>$u}%uNCi|b~d|5lq~vppX1!c^B=bTPJgra%vwG}`F`8hEcWmn$7W2cu)b|s zk$q`vr0D6GxI;-n=|F>)Nc-1+?}~Lb7t3^FPZJ|J=$>* zqh?s;q`QAJ>?Gv>DSfZis(4y%F8jg$@0Z}cTY?=9N^`HzJFb1LxygK5Wt4w{_V!Yd z8gbV7dXN0$<{uH=b4$^*YnkA?sZ;dJ!{k5D`0l8ZdbsdIY54MgE8l-|dwnjgvYSuu zk4#;7w(hPwYpz_AS+wV_XW@;;rT4j#@BDUH&2M*i)t97Mf6G;zSD$ND{c!5lSAOx8 z^D~~h_$?Lmzb~;~{>GBa>S3iH_+-p)pAD<=myTtU={vwa|H`^v>xjqM95H)kZ&gg4 zeW55fnMHZ`J+4J;7BhErYCh!L7O`H=fbSmT{n<_7UbTPjmfw$w>EQdZph-MVhijqf z*~|_#sek<)^{QH@tbgy7F|X`?y!LP7)$L`ye02^7w`=@(Z6BX6eKLw|>+FNZQP->= zN0$EBt$XwDl(YMHo@2N98Mky(FL(KJKdyV;TW)R2_fI(Z?3l#n%TZAWy~CD@T)wwf z=*U84wJ9eg3btjepTA_yvdAm{Y>Kacy0)q;+wN?Ree_FD2G;sFTy`sCx#Oo!-*`bR zEaUM)WiH)CkJm0Z%3wG-fT!obfrH4|Q(XTR_t*b^b#=9T{hyDM{p;UbKS<`XS5n+AHxW}^a!FRK+W7ofYvN7JbM!e_M4dHy0bE2^~BJ=j^_hx)B<((jvewY@{+{>rT1$%}4!P8TlxaOB|f zE!qFWLq8k}>fOG%iQlYopGb{tx1!pzBckR;C%ZZ=ngrVJEy~#U>(%*F@Bh8sUjI?{ z!%4dz8To=yBBdDTfJo3 z38}gZrd@6|?Q-4V_Qci8QCGhm582$BQ_4_#Ia;(h!$6#u%kE=tPoZlhuffufYTG`3 zQ}4A3P=6aHV6G4~=R$Lp!IHJNcIGWSZYb`N&lR`x-hA8YZ&vqZ*lR5K>>gyi3_f^V z_Qu{Hd#ge|>|^}#An?OV9pl+go_cNe6kCz`Kvp;I=XKuuH81nN2b;;Wug`lOf8Dk# z@4-8du;8ehAM;CDS`BA0e^N?d;w2AXb@}r%)QS$vu}M{WYRr%-yyV?%+N>P; zc;3Hj60X%e_iN1NZolTQvU*~@R_gDsf=jDQ_pG`*%ik<|$DB7mAG~c~blW7yfA*zc z*7b8cJ|tum8g#j3JdPFFlb-SklvLJRG#cGX@|k6SVUp+BJogI=e#xBtb-emJH|u+g z2g=14Zv;Qt_NzLnvr%K#HNmsmzxM92`0=zhd=lf4gNq+r?~J?na?|TBzYRuj&#_v3 zxg2#$VbA*wo8NO?EANkbU9-d8<@{#PRT{IV>+8$1|6%|5^TBL&n?j%EFRBD%d~=l} zZ!gN2_N98Jel+v>tuOa!-;a8>$noHM$)_3N7mc)@#Pa5Z&Z(_;uH~t?ztCP?#{b!z zu!ffMwr*dWx7F?@hXMmW9AESx{7$a^-KaC5I@=@b-0U}#8ZUEgm@_5n`)x&$KY1EI zzTexu@=B$4)^_G^^=tBd!(={ytJH>hqBj5d9S$-xFfhEC&j0VPiTt16S4bE4gU~d^KWEo>1xIa&9s zUU}sEzUK3~s1q-rOc!R=wqj&pkm*J#uvA#{y&{q`FR5*D^T=5?X-=A(gJ1jBaPIZ> zUUti7+nmp};a;yC9erb0`qx{D;IfN-C((lq5!DCcuCCku%V=$c+pgo*C$D*h+}jw= z*L>#u?(51gDo&i8emeTe?%mp4ebegJceUKvR%hOQ%r*QwWAL8J+&>&9q4rn`r^}BX zyq-PFPsiV_nAfv&-F>Tz?D@84FJ3EX%1_ORjQHnre@ge41vf9v+s2spuc0Zv&Anf) z{Oi8%PkY5aGWzei^k8Yh{>ip`XYsrjdHMYC$rO`W60_o#UNQPxGlO@@l)Z2MUz_;( z)qBp~=nH+vqR9q>QyE_nUI?56N_-_y^}+k4LZIOBXV_VxGMT#oKq-~ami!hJD)+7$@St~eaRO3e7G}TmO1RG(ezq*-M}XK zs8>66n!@+`w)BYGYhLx&;JIWub@P7iZ?nsf29vih5OrK-$=8$oB(|eOiU;Dqm6X&{D zc%c0A!QBrvl6tyIKdmxc_A=1q(^0{*$Ge-wtK2W?`qtW?CYzrIoK$n({w-=9uSEds{uq@FvL1Ul_L{uDb_$CTNZh+eO36EnMXzUgj=rV+GpVvCa)xSIU{WAU&D)UUVKu!da6OA;?aZYT`YVp zGU{gc&T-k@VEGmFA++$xk&vZRqLa>AZnJ1QxMsB$QqjF)&DXUjyO`xz_y3%@CTqc~ zQ_7J?Kd!X$bNhXr{cx7~+(mQ0)SbE}vPZt^;f}JJ^!|%0L-gjaTWESR(8Sy27b~<0 z(CNatY$~J)aLjy%YS*%`pZBV-TwArxe%Y+I)5~+_NoB5K3N$%xJ;_v8sN!P9^yc!l zp|>i2Pj-`CFO%?s{S-zVbe3{>8d_1*3 z@5eLma?ogzMa5Omn%M6eXSW=z%bERA+nHC~+GO%f3CHq6*86{AWY%V|zSk9gnf^Vo zX{Dh0UXuu8i%DB`y$n>#Hp<;JoA3L4?N{NGSx@GB-K?v1$uw)K2;FTuKWAHXT;iNX zr9z9Zv&jgZt$Z)RIj5hSf8Au^o<)M-@qzWpi1C4Mr@68-ISSt@h}bZ^ncEc4w0F_v z)a#;i7xYiCc{cCS7N$2}b~k;EJ(E3`|9gkSL1vltP5L$;wW8cw_pEMQ+qL<9{)&Fa z_tsyYw`L`-D02- zQwDG?2C84d^=7~a28|zcA9VGCLGFKZ5gP_OP}lYVJAVVX%ab3sMS_0SkO* z*;;Yg`u?8PVntRzpSm3E@?&5C87=bXUD4;w(aY`U#8nrcefZ^5NUi9Ps|i%cJA!H#qA5H_$I8iusD0ISY)rzsjzUBz|$IfRk30vuT>}cr}C@amELbE zwm_!HzxnR6`&avfif$j>7JvU~&(h0Xv*+!5YrEq6PL0ca^|cxzHU}Ctevn?@6fduF z(w6%6sbJG%ZRKwXaeDWq1sDAO9du-~mP*b?rnq;j4`)AC{PeXyeRJmJiuSAjeLF*n z&92&T$uWF$IA~8~onbTe*3OI{AG}fno^&>`i~M;Po^Z8pD)YXo#$|G+iyzv}`SwFd zh?T#g>HdtS_bQLh$IUq^`sZf1^hvINd0`dzx2o^E_QEP@nc$bAQ(;xUZ%=FfHMzNl z$NJ)-bv&zf7{uE7KIon4`EkGCoW<6Ys@ng|-}t?1yVa2zn{96kKYi>q)cshttyp8X z;q@yULN}i2t&y6Xsj9u>$+=CW-CA|gqUp$f4=)H#vHU}lSs|dTfC<0|DRhnRrY(p2Zjyq zTz0WX=I^oV)UMd-oF(F`d`DX|{@oh8XNK>aJx;Yfe(-vo-(TfDEupmz_p~Ln;wyMI zq;8xLQF_?-N}BM~N$!Ofb_Ry?Pf3KAZ2R@#@>(zbWjE{HjkdhrQ)$IDHT=EUg=_yj z4TW0gPS&|H^}XhjV~y?na%RtbJ+iJ(y!OO=0;H6`b9=9eiTgfw-U1`edxn!l{}{Gj zUH}@3F}hyAPcGm?__>S_W1OSMEfL+I&R>Y6nVsT&xtq6hC6d3nd23gkTs0->*6x+- z3_k=ZMLfUiKWkz-_kFG9GwN)2s99PskJR}2An?P6*6P5Z-F(~C7B;TAaLm~~X4k@_ zkFFh=?fjtc;!Sq@KMxMyUbsD1qk=)iCeTFDSO2{6-iCj`I;8p)L0hIU&?ps zT&LKph(BtRlbX1nEoxj_DJ&Gn=(*N2Lt*unkKfdL`*?Np9JW|6>bI?az{XqVe z4Qs8KT==IMJy~EeDU>TK{)BSm+!~wYStllMdcCPktUBi4m5#~xw3dY^U0$+d`Tb4m z@{6+iDps>?Ih%fYr@-mgBA3^-pS)sImGiued+pB$Z_B^;q_l3m6qD@v*6Px%KfA+{ zq~9-kFg-s*m#yL`=M=+54|;z$1gN`T=nDM6z`p3gbcgjXN^X|Asz=P6(f)EmZLf8W zRbSFy@dmxO<+a~eY(3Xr?XutjV_*0|@p%=Zx{ICeT|E$X^yYW-E5{qY%l;}pB>T@N z{mXF%+dalHIgc{$TQ3q{*Vg0?DmfooWTv^q$?RjE_fI0B{=UGv-%~}n^;9R`q6kPJwm&Oe~reDCM8yR#IR zPmcE!S@VL0fuW&^-QnPSJ#+irrN0ip+-iPZ#!uh>YItKjuiM#xAIoo6e!lAb^=L-I z+dFTpt0cT`vRvzGzMNz0eo=d2PN}QV)}BX;u3wGY9;fs(WL-{BVBlNt?2SiwWGYAW}+Lo9q_4q9i` zO}eZ5&y4R!)cy9LfDg4=Kh8#PYF{3-^TnO(A{rGA2j2&>{#t)-%8v8iOQOE#M5x@8 z-fvrBZo?(_pg@H19AH+wUuE(7uXD=IT1^z)d3rhL9z$czf96faCEF8!HQuw6tky4H z?R@5V@@+YF`TG)=h4Ry_+y4~oTbZ~zX{Pi~6Sb@J{spuh+a4MFg#BFf`%@KrZBK+(;(Ec-fi`@ z|H~=U*-vX57Jh0y&JZoe-5F|r$0gS+P;b@j$Q_pg&MY@N_O>gk7BR1PtMs0YZ0o7) z7cV^wPft!#;j&&H9r3C&Q%%gWXcoh-&uS`LUwdSKlJ=;QUSA{}_3rIIsaekrGd2C5 z^a%!k5Z`Q64^GymcnjPHb;xvG#!Jw2-3p_eCi9x=l>F_9J&(=e{Gj7)zF(&|P8T|8 zwZ1N?PwDHLqOOl%KFe{r5#n%4G9=otPsR_O|!k@OSQ(P)JL< zV;Ci}=W7M11+yqaXmLgjw$k==>>;P{?fhJE-#1Ntw&HKozR)tmr&Sxi@xPsumiVvO zK7xalUwV)D)~&CnT)VRHV_DdT)#h8om{&bI(OJkEf8Q~O0;Iz%G0hFe{8nxFpYT{^Z$rcse*RJ?iq2m6^{eX z&Yi4t=2)7p=V7*vMUA~x!i(No-I>+Gw#iF6zy8vTYZsz;I>gi;94!#t^FDRDuhT*1 z19{p%Qqv~yiODoO$z;mAXYo<(|GOT}*1!9q)Mus3GS5Q){i*(TE?0Xj z*29Zjzsq@752s|w*={R7RKZYvVnOKqiMDu{rQj>&GGC^@~zNPVAPtvC882zqVfzAND!d`|Xem_j5hSEE9Em{orLkQ`{|7#M8( zFU=*#AP-XU05l5&8h-+}8$e|*xXy;?4fs&q6t(B&-28i?+?I3y6fH_{RcBye09jmD zPW&JZwiO}Yc6}A3m3P-5)+mng{|)n zzw_lw?t4a1OEmPLebtv8tG~xidB3%Tzv9tUHvQJ$bKktYno_ac!(QG`ZSln;7rtmM zo%Zti;T(mfZ&Lqn@0(-&R55d8bi*x zdOM%)v)W)b@79Z=lX2otJZ?*I$vtoo`Lp@?dF##!Q5)e&?^(6xsI7Y-YifV;omjmz zd(GPS&YBuO>_q=O)4h4D^31W%&ukBG6Wvo5YWB(Y`N7FQ16Ol799+Mj@3UL^{+*&L z)Ss8`jd2Clk>(2?Sa<%|ES_G-TL0~l+oq=dvqkTfzwKOax~p$SCd=MAo2JGEXRdj* zc6AkJ^cTjcE=m3L)u444#*-h_D_gklj?HbqaHD!}v}}@{-0g1`KYu`{T5hacdHXGF z5k~c?fVOS7w(Z|L!}8ecyUR8o>U|rsBc6Y$t@vgu=Zl=_>S1OpY+j3hymsDJ`RL_hVb44TUC78t&z4h1ATupfA2wax{_OIm&kDUw>a zOJ;RmM0@77b`9%2G458-Mz9*zM({No-`1|kc_RC73H!MuI?L(bmN{XeId9^l!c^t2 zE5Cf1_PtBqVc)9B?4>0;0xsX$aZ}IbZFH)h#Lcf^0;UH}s#F~RaKA9JU)cRc1Gst8 z9P|HYmBYb!%f*3bA4uxHp8Ii))t|f5-qf9Xc0cC%q;uc3rrH@OS}mH~^j+xv@nm~& z34N1QZ|wus)j?gE@5A;`y6?y{uDevJP=O*8f^U6Vx)E zP@?aZb^YNrk^KR?w|%@I*cx{8W7vhHh8dm~T5eCnwp%R9$OvR`xu_%U`Z&9)a-L-T zHEyT+(_Hu7C2w2vfOUO@AmgL*(%%anY!zO2NoH$&0H0WX*UhBpiRt}Z_soqd{<`$4 z@X1-->Ar<%9KT?_-`=#oOIe+3`$4O{RUv$K-iseFJ}_|Kgm3+WNbl!)*XuuLmuN-( zEf?*ZyC~y{!G#sY<`@2Nov|xMq*uI3<$A%?;=jV#R~!SLNgeIE5%ybQcGD73OM%15 zQ?;vNqHSNC`1kClPXG5*J*gLud_5{MHy>%ewY7J8- zUwcc^S8Y>blsL)t&*8ZqsL}k0SLM>N-5Nh4R=@dj>AWep2$%mgPy9RYv5VV`AgwUf z3-Cn?FA}W^;vZj}bLS}2d+Rr~zhm#)X084FaM1(C2OJ_b4?D3<9K`f%FJJV4k#FtW zithfrA02KDT#%M=d-AfGf|Z3L!Qz+YnLh||vi5^ow8$kgw22#@0|CGHFMPoG0X%15 z2kKsd%4|^m0xIUgNCV=vqTIhDT$Zi)_N{eS#0Iu)RSU~bQ_JMm`r}$18H@9M7@Izh|5ieNcP(g5Q^zS@|`7{M%_>7j1p( z?Ehjpo0*FrFec0=X4Ty7+OOBPh9%rO`1pv^3#8Jzz3hthlm9(;#rJ(xeD-)_ZvW)| z{GQCt?VIO1*IC?0j8lkLXgEjbvfrx;2N&1dD?R6OeSf`h%k!kL^o;3#Iz_3?r-DoW zY}>kN;-f-lw^vfer{rv=-t%lvKC;gy>UODj_9ST!Z)yJLYWIzm*BX5cn~>qq9U92G zzD~M7(fJ_rfpgkKw4VKI@0)*#H#_~C&G{*JKjYd%8(+1RK1i$W*mU@G=@R8gH!9 zvz^~=WvqMD}-53>YYnI z@AX>Bf2Jot&h21z=l91OlT*ff~K(7Zh}l<3qS5l-2JdS zy1hythbAsuJ6rR zYMG@!Yr(>`N!xCOJ-NBK>3c6voc*z#8-#0j_BXX($<_=!s2;b%R`W;3>Nn2dRc@)P z9?NC^u;!9`aDzaZefE&Fl?^Y`evNQ$%I$iAD*z)iD=u6eid)WVr>1!bS596PkYOF72apPWIRJJ&AwLF|515-jU7BPUUQ$wc}C~Z54V2Xxo%>HxQ3D76rqOY+ViJ2 zaEVq1v3NYOyIwOpw!%?l=L}=W2t}H6a{Hh8AJ6pOUjbT+lv_QgrY70Pd(YgrGM^6r zT-LU>D>d)Q*RlKR z*V1C2SM~(XG)b~O%053Yw93%8RqA9wmCrAOn9S7N?%y4y5*p#3C7#(x>4vV>nm_Hp z_pIk0YIzr-Y2m9jXey3hk#i@IZ=Y_WKzdh-$|IHc`SG#wYKhJjCvth2t zp6MB#PS8=^RS%bnLKg*3k+=E~bm)Zoq|NaMUX?tFKHjyJKlbLfsI_giAJ)zFQ%izb z6x+4vqv>9o^F8{nSr_h4WzE;#rmJ1yX!mShb-dO4l}-Ftw|%;^e$j)uKfW%_HWgsk zzmb_PH1Qt;IQI9fcwh}%Co}7DAGU=u8dB;{4)Ze^hRhB7@6B=ci-uI?H|MX;`e*)@ zopv|_UM$mjWQ&U1#T)UFiv^?BOg$C)^cmL+(X6XA2Yzpu)$O+7^7$0U4?LhQZUFw} zEiBirZ{2Yv>6YxtEWTx1*fp;SifoQt`|Z@iUeH*;pDM-vl+sThdoq>G-xSWD_@Dx1 z{55{vw+|lI9ywgCxbQQ_%72yf@|epvqcb=Czu<9gOO)w%{rmGP%Bp61hzl`*Q}BKH zD%a(w*J)cFJSC95@tuGsxVtQ~3j2&!^S7^?j;Vttr$7Up?RS^Ww3svVP~ZnhFoOE$ zpb=AWsR8Pi|0&xEU)ssb%HO~*2VM<=HfIGYhd@SxOApW_7F+;jI2EK9E&y&8f|hcE r+yNSJg$sa+Adn##0&r8mb^hlUf06FE%_#BgTe~DWM4f+q_@! literal 69724 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%0DMZnK6@-kRTimon3~-Bn!v;LfaBv!)q7 zTYLTR%Fp(tit(Du;x-v==kRZ4{&hTksgqy2ucy<5mx{}6*p{BX`|QciY+0My2hLu5 zMUtf5BQ9;aU3>nK4BR^%gaUa)PAg+Hcqn&H6uh8%5^TR=>r<_;a8+&GM`~j|oD$du|Z7a;xSu4aV z=v_Ski~ZSma=w>NH0-gW#r z*Wz=D0uzr&HLjbt$Sm*6F}S1Z}f-QP%Zu`5Jw>RF#5x9ouE$`4P^Du=8rn;Bhjlv(a;r`PO> zMU#B}#CS2MNW= zClf3wp3fkW>?Ex-hIVq&hEwjN57QoFPf3dyYaiv z?0UNs?>g@Fy?7f@VRSb!+~evbuS+xKYfe3LlCBHu60KguAEctPRHRy^Q_!1UNcT$TA)Q)yTul7w|>*T zN^-p~=gugH>obcT7IFkwa|#Jv(z4=N^4u3}vBciLQ&jtm#M0}FPM^E%w~p^x7~@oD zhK-XKnQ#gT2^C3rthOxVzIc1Sb=B_!JyDED*OtEAWv*^$xWE69kY}JG8&uJA@B*{H zkqfsUUhh-isx2C9u3iYLTNT?Ix0r#}1cDcmaTbD>#zm!qk`H8o#EZB0TkrL?$Q*NK zmVk(~ym)&eQNi|~4#)wZ^I{bg*XAu%2iKLWzPLC!@iBwOqd6^kR6vsppu@!vJ3s|r e{w$REFW-47P^vZHxDx{d1B0ilpUXO@geCyBYR#$u diff --git a/doc/images/qtcreator-tests-view.png b/doc/images/qtcreator-tests-view.png index cb771e06cf792109d1f2c35992d2b9bb30022e41..0faf49a5a4119b40b726f8c4a0cd8ecf5068ad26 100644 GIT binary patch literal 8796 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 literal 30506 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 \uicontrol {New File or Project} > \uicontrol {Other Project} > \uicontrol {Auto Test Project} > \uicontrol Choose to create a project with boilerplate code for a - Qt test. + Qt test or a Qt Quick test. \li In the \uicontrol {Project and Test Information} dialog, specify settings for the project and test: @@ -59,17 +60,17 @@ \list 1 \li In the \uicontrol {Test framework} field, select - \uicontrol {Qt Test}. + \uicontrol {Qt Test} or \uicontrol {Qt Quick Test}. - \li Select the \uicontrol {GUI Application} check box to create - a Qt application. + \li For a Qt test, select the \uicontrol {GUI Application} check + box to create a Qt application. \li In the \uicontrol {Test case name} field, enter a name for the test case. - \li Select the \uicontrol {Requires QApplication} check box to - add the include statement for QApplication to the main.cpp - file of the project. + \li For a Qt test, select the \uicontrol {Requires \QApplication} + check box to add the include statement for QApplication to + the main.cpp file of the project. \li Select the \uicontrol {Generate initialization and cleanup code} checkbox to add functions to your test that are @@ -135,6 +136,46 @@ \l{https://github.com/google/googletest/blob/master/googletest/docs/primer.md} {Google Test Primer}. + \section2 Creating Boost Tests + + To build and run Boost tests, you must have the Boost.Test installed on the + development host. Typically, it is installed when you install Boost. You can + download Boost from \l{https://www.boost.org/}{Boost.org}. + + If Boost libraries can be found by the used compiler and build system, you + do not need to specify the include directory when creating the test. + + To create a Boost test: + + \list 1 + \li Select \uicontrol File > \uicontrol {New File or Project} > + \uicontrol {Other Project} > \uicontrol {Auto Test Project} > + \uicontrol Choose to create a project with boilerplate code for a + Boost test. + \li In the \uicontrol {Project and Test Information} dialog, specify + settings for the project and test: + \list 1 + \li In the \uicontrol {Test framework} field, select + \uicontrol {Boost Test}. + \li In the \uicontrol {Test suite name} field, enter a name for + the test suite. + \li In the \uicontrol {Test case name} field, enter a name for + the test case. + \li In the \uicontrol {Boost include dir (optional)} field, + enter the path to the directory that contains files needed + by Boost.Test, such as \e version.hpp and a subfolder called + \e test that contains the test header files. + \li In the \uicontrol {Build system} field, select the build + system to use for building the project: qmake, CMake, or + Qbs. + \endlist + \endlist + + \QC creates the test in the specified project directory. + For more information about creating Boost tests, see + \l{https://www.boost.org/doc/libs/1_70_0/libs/test/doc/html/index.html} + {Boost.Test}. + \section1 Setting Up the Google C++ Testing Framework To build and run Google tests, you must have the Google C++ Testing @@ -310,6 +351,30 @@ failures into C++ exceptions, select the \uicontrol {Throw on failure} check box. + \section2 Specifying Settings for Running Boost Tests + + \list 1 + \li To specify settings for running Boost tests, select \uicontrol Tools + > \uicontrol Options > \uicontrol {Testing} > + \uicontrol {Boost Test}. + \image qtcreator-autotests-options-boost.png + \li In the \uicontrol {Log format} field, select the error report + format to specify the type of events you want recorded in the + test report. + \li In the \uicontrol {Report level} field, select the verbosity level + of the test result report. Select \uicontrol No if you do not want + a report. + \li Select the \uicontrol Randomize check box to execute the tests in + a random order, using the seed specified in the \uicontrol Seed + field for initializing the randomizer. + \li Select the \uicontrol {Catch system errors} check box to catch + system errors. + \li Select the \uicontrol {Floating point exceptions} check box to + detect floating point exceptions. + \li Select the \uicontrol {Detect memory leaks} check box to detect + memory leaks. + \endlist + \section1 Viewing Test Output The test results are displayed in the \uicontrol {Test Results} output pane diff --git a/doc/src/overview/creator-only/creator-overview.qdoc b/doc/src/overview/creator-only/creator-overview.qdoc index 790ed4bf0d0..09ef2766e4c 100644 --- a/doc/src/overview/creator-only/creator-overview.qdoc +++ b/doc/src/overview/creator-only/creator-overview.qdoc @@ -116,9 +116,9 @@ execution. In addition, the QML Profiler enables you to profile Qt Quick applications. - \QC is integrated to the \l{Qt Test} and Google C++ Testing - frameworks for unit testing applications and libraries. You can - use \QC to build and run autotests. + \QC is integrated to the \l{Qt Test}, Google C++ Testing, and + Boost.Test frameworks for unit testing applications and + libraries. You can use \QC to create, build, and run autotests. For more information, see \l{Testing}. \li \b {Publishing} diff --git a/doc/src/overview/creator-only/creator-testing.qdoc b/doc/src/overview/creator-only/creator-testing.qdoc index 39bae4ba17e..10df1d7f31d 100644 --- a/doc/src/overview/creator-only/creator-testing.qdoc +++ b/doc/src/overview/creator-only/creator-testing.qdoc @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2018 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Creator documentation. @@ -60,9 +60,8 @@ \li \l{Running Autotests} - You can build and run Qt tests, Qt Quick tests, and Google tests - using \QC. In addition, you can use a wizard to create projects that - contain Qt or Google tests. + You can create, build and run Qt tests, Qt Quick tests, Google + tests, and Boost tests using \QC. \endlist From 41ade463da39473bae42fa4755813250f3356fd6 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Thu, 20 Jun 2019 17:27:10 +0200 Subject: [PATCH 14/59] Output panes: Support keyboard shortcuts for zooming MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes: QTCREATORBUG-22567 Change-Id: I3c7419c7b464c329d8f8dae11db9a0b01e51f32c Reviewed-by: André Hartmann --- src/plugins/coreplugin/ioutputpane.h | 10 ++++-- .../coreplugin/messageoutputwindow.cpp | 2 ++ src/plugins/coreplugin/outputpanemanager.cpp | 32 ++++++++++++++++--- src/plugins/coreplugin/outputwindow.h | 1 + src/plugins/projectexplorer/appoutputpane.cpp | 8 +++++ src/plugins/projectexplorer/appoutputpane.h | 1 + .../projectexplorer/compileoutputwindow.cpp | 2 ++ src/plugins/vcsbase/vcsoutputwindow.cpp | 2 ++ 8 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/plugins/coreplugin/ioutputpane.h b/src/plugins/coreplugin/ioutputpane.h index d219e2aefff..b7800a65c84 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 7aabaefbbc9..c7c752e1556 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 951fed419b0..7cf2ec2c90a 100644 --- a/src/plugins/coreplugin/outputpanemanager.cpp +++ b/src/plugins/coreplugin/outputpanemanager.cpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #include #include @@ -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 88692d5ab74..204a1106a93 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/projectexplorer/appoutputpane.cpp b/src/plugins/projectexplorer/appoutputpane.cpp index 7631f26cac6..df9fd503ba0 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 5fc88219fcb..6d7936042ee 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 91cc2fb22cc..d92bcc4bbe0 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/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index 4c559a9c742..76ffdf46ea5 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -302,9 +302,11 @@ VcsOutputWindow::VcsOutputWindow() 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(this, &IOutputPane::resetZoom, &d->widget, &Core::OutputWindow::resetZoom); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, this, updateFontSettings); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, From d27f7c675e123aa83fb85e286832b54bad6751f0 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Fri, 21 Jun 2019 17:43:48 +0200 Subject: [PATCH 15/59] ProjectExplorer: Make varsBatCombos Squish-testable again Change-Id: I4656800f80022652f9277029c597754c9263bf66 Reviewed-by: Christian Stenger --- src/plugins/projectexplorer/msvctoolchain.cpp | 2 ++ .../system/suite_general/tst_default_settings/test.py | 10 ++-------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/src/plugins/projectexplorer/msvctoolchain.cpp b/src/plugins/projectexplorer/msvctoolchain.cpp index 350ee1a55dc..24b528154de 100644 --- a/src/plugins/projectexplorer/msvctoolchain.cpp +++ b/src/plugins/projectexplorer/msvctoolchain.cpp @@ -1331,6 +1331,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) { @@ -1476,6 +1477,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/tests/system/suite_general/tst_default_settings/test.py b/tests/system/suite_general/tst_default_settings/test.py index 928f890b2ab..3270f619e53 100644 --- a/tests/system/suite_general/tst_default_settings/test.py +++ b/tests/system/suite_general/tst_default_settings/test.py @@ -121,14 +121,8 @@ def __compFunc__(it, foundComp, foundCompNames): pathLineEdit = findObject(":Path.Utils_BaseValidatingLineEdit") foundComp.append(str(pathLineEdit.text)) except: - objectString = ("{buddy={container=':qt_tabwidget_stackedwidget_QWidget'" - " text='Initialization:' type='QLabel' unnamed='1' visible='1'}" - " type='%s' unnamed='1' visible='1'}") - try: - foundText = findObject(objectString % "QLabel").text - except: - foundText = findObject(objectString % "QComboBox").currentText - foundComp.append({it:str(foundText)}) + varsBatCombo = waitForObjectExists("{name='varsBatCombo' type='QComboBox' visible='1'}") + foundComp.append({it:str(varsBatCombo.currentText)}) foundCompNames.append(it) From eab4e056bc27a2e8fa219f5be70b2b5e034731a1 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Mon, 24 Jun 2019 16:35:43 +0200 Subject: [PATCH 16/59] Small fixes in French translation Change-Id: I86816eb8e1d0f7b439532b9196aa28a8119e5234 Fixes: QTCREATORBUG-22613 Reviewed-by: Jocelyn Turcotte --- share/qtcreator/translations/qtcreator_fr.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/share/qtcreator/translations/qtcreator_fr.ts b/share/qtcreator/translations/qtcreator_fr.ts index f1677fa36e3..95808e6d7b9 100644 --- a/share/qtcreator/translations/qtcreator_fr.ts +++ b/share/qtcreator/translations/qtcreator_fr.ts @@ -1,6 +1,6 @@ - + Application @@ -42412,7 +42412,7 @@ au gestionnaire de version (%2) Set Message Tracepoint at Line %1... - Définir un message de traçace à la ligne %1... + Définir un message de traçage à la ligne %1... Disassemble Function "%1" @@ -70891,7 +70891,7 @@ réinitialisation du moniteur Get Started Now attention à la longueur du texte, doit reste petit - Démarrer + Démarrer Online Community From d54d1c34b8a9b2cd26954f7e2872552aa0bc7707 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 24 Jun 2019 15:41:08 +0200 Subject: [PATCH 17/59] Debugger: Set tab title when attaching to started application This amends 75b501be9db68. Change-Id: I186b276b82ac5793c58be9d37c36c9d49dda1edd Reviewed-by: hjk --- src/plugins/debugger/debuggerplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/debugger/debuggerplugin.cpp b/src/plugins/debugger/debuggerplugin.cpp index 8c882ab99f8..699ea751bda 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(); From 26d618024cdad6bb99975e913558609679dc97c5 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 24 Jun 2019 16:47:39 +0200 Subject: [PATCH 18/59] Clang Code Model: Fix product name capitalization in UI text Change-Id: I81d7e411d47e64d5304f30ca295c3f4b1f0d51b5 Reviewed-by: Nikolai Kosjar --- src/plugins/clangcodemodel/clangcodemodelplugin.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp index 12efff6c6ee..6c5ae3cb4bb 100644 --- a/src/plugins/clangcodemodel/clangcodemodelplugin.cpp +++ b/src/plugins/clangcodemodel/clangcodemodelplugin.cpp @@ -145,7 +145,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( From ea76ca45e4971e4c9e3f2b144b846fbbddc8c755 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Mon, 24 Jun 2019 16:58:50 +0200 Subject: [PATCH 19/59] Language Client: Fix UI text capitalization Change-Id: I6871bb6f91b6a7dfd8b0a12973e709193d6a8a99 Reviewed-by: David Schulz --- src/plugins/languageclient/languageclientsettings.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 50465770d9c..1b09b62eb0d 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); From 5be6e8401670c8c6117e95290276b11f4b633a63 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 20 Jun 2019 12:00:58 +0200 Subject: [PATCH 20/59] QmlDesigner: List all fonts in project We iterate all over all ttf and otf files that are found unter the project directory and show their name in the FontComboBox. Task-number: QDS-100 Change-Id: I45c4d512783d5ecc4a646860c08e6088bd712798 Reviewed-by: Tim Jenssen --- .../imports/HelperWidgets/FontComboBox.qml | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml index 16de1a90ef7..5d9ae328d24 100644 --- a/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml +++ b/share/qtcreator/qmldesigner/propertyEditorQmlSources/imports/HelperWidgets/FontComboBox.qml @@ -39,7 +39,35 @@ StudioControls.ComboBox { onTextColorChanged: setColor() editable: true - model: ["Arial", "Times New Roman", "Courier", "Verdana", "Tahoma"] + + property string fontFilter: "*.ttf *.otf" + + + FileResourcesModel { + modelNodeBackendProperty: modelNodeBackend + filter: comboBox.fontFilter + id: fileModel + } + + function fontUrlToName(url) { + var fontLoader = Qt.createQmlObject('import QtQuick 2.0; FontLoader { source: \"' + url + '\"; }', + comboBox, + "dynamicFontLoader"); + return fontLoader.name + } + + function setupModel() { + var files = fileModel.fileModel + var familyNames = ["Arial", "Times New Roman", "Courier", "Verdana", "Tahoma"] + + files.forEach(function (item, index) { + var name = fontUrlToName(fileModel.dirPath + "/" + item) + familyNames.push(name) + }); + + familyNames.sort() + comboBox.model = familyNames + } onModelChanged: { editText = comboBox.backendValue.valueToString @@ -97,10 +125,12 @@ StudioControls.ComboBox { target: modelNodeBackend onSelectionChanged: { comboBox.editText = backendValue.value + setupModel() } } Component.onCompleted: { + setupModel() //Hack to style the text input for (var i = 0; i < comboBox.children.length; i++) { if (comboBox.children[i].text !== undefined) { From 241c35272399f8e87ebeefc58f3d83ec808a8f61 Mon Sep 17 00:00:00 2001 From: Thomas Hartmann Date: Thu, 20 Jun 2019 12:43:23 +0200 Subject: [PATCH 21/59] QmlDesigner: Allow adding of fonts This adds ResourceHandler for tff and otf files to the resource browser. Change-Id: I8fc757162266d79d656ee58bd0f948107d58db4d Reviewed-by: Tim Jenssen --- .../componentcore/componentcore_constants.h | 1 + .../componentcore/designeractionmanager.cpp | 7 ++++ .../componentcore/modelnodeoperations.cpp | 32 +++++++++++++++++++ .../componentcore/modelnodeoperations.h | 1 + .../propertyeditor/fileresourcesmodel.h | 1 + 5 files changed, 42 insertions(+) diff --git a/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h b/src/plugins/qmldesigner/components/componentcore/componentcore_constants.h index 028d3a21b4b..0535a8f52ed 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 6edbacec365..37e1fdfd539 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 3c31998576b..9f880c453b5 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 e3396a303e9..52dfaf6f1d7 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 cfffeeaca4d..f6866310796 100644 --- a/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h +++ b/src/plugins/qmldesigner/components/propertyeditor/fileresourcesmodel.h @@ -27,6 +27,7 @@ #include +#include #include #include #include From b52789bce0a32eb40396857fa975aef9f2c17f20 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 19 Jun 2019 15:16:15 +0200 Subject: [PATCH 22/59] ClangTools: Make building before starting analysis default ...when switching to "Custom Settings" in Menu: Analyze > "Clang Tidy and Clazy...". Otherwise we might run into parse errors due not yet generated source files (e.g. "fatal error: 'ui_mainwindow.h' file not found"). Task-number: QTCREATORBUG-22382 Change-Id: I6f499fa8f8ab2fff08d19165e474d14305cfded5 Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- src/plugins/clangtools/clangtoolsprojectsettings.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangtools/clangtoolsprojectsettings.cpp b/src/plugins/clangtools/clangtoolsprojectsettings.cpp index bc8a3cabb6f..55573c8ee60 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); }; From 64e842759b73d2d51cd58cb935dab66171ac4d39 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 24 Jun 2019 09:10:34 +0200 Subject: [PATCH 23/59] Unittest: Silence most of the warnings on Windows There are lots of warnings coming from including or linking against LLVM which cannot be influenced directly. Silence most of the warnings to be able to work with the mess. Change-Id: I2c4adec14945ada878bb1e6fda2f06e6d56007e2 Reviewed-by: Nikolai Kosjar Reviewed-by: David Schulz --- tests/unit/unittest/gtest-creator-printing.h | 2 +- tests/unit/unittest/unittest.pro | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unit/unittest/gtest-creator-printing.h b/tests/unit/unittest/gtest-creator-printing.h index 897a9f2a280..b874fc90e4b 100644 --- a/tests/unit/unittest/gtest-creator-printing.h +++ b/tests/unit/unittest/gtest-creator-printing.h @@ -49,7 +49,7 @@ std::ostream &operator<<(std::ostream &out, const CompileCommand &command); } // namespace clang namespace Core { -class LocatorFilterEntry; +struct LocatorFilterEntry; std::ostream &operator<<(std::ostream &out, const LocatorFilterEntry &entry); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 8e0a4d65777..63980ab0554 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -36,7 +36,7 @@ CONFIG(release, debug|release):QMAKE_LFLAGS += -Wl,--strip-debug } gcc:!clang: QMAKE_CXXFLAGS += -Wno-noexcept-type -msvc: QMAKE_CXXFLAGS += /bigobj +msvc: QMAKE_CXXFLAGS += /bigobj /wd4267 /wd4141 /wd4146 # create fake CppTools.json for the mime type definitions dependencyList = "\"Dependencies\" : []" From d1c226d70bffba666806c1d1de8f2d8718fc3140 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Mon, 24 Jun 2019 15:06:12 +0200 Subject: [PATCH 24/59] LanguageClient: Fix file path filter handling Avoid sending files to a language server without a file path, like temporary generated files. Only allow this if the file matches the MIME type of the language server. Change-Id: Ibf71a7196c387a2c8bf345db24c0005ba8fbdfb1 Reviewed-by: David Schulz --- src/plugins/languageclient/languageclientsettings.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/plugins/languageclient/languageclientsettings.cpp b/src/plugins/languageclient/languageclientsettings.cpp index 1b09b62eb0d..a1b8db21691 100644 --- a/src/plugins/languageclient/languageclientsettings.cpp +++ b/src/plugins/languageclient/languageclientsettings.cpp @@ -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); }); From e6ede1d99756de7cca4ae8429664be0ffee1e0f6 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 18 Jun 2019 10:59:25 +0200 Subject: [PATCH 25/59] AutoTest: Limit condition for test execution to Linux gcc and clang are handling builtin include paths differently. On Linux the default should be gcc - which works fine even if boost is installed in /usr/local. On macOS clang is default which does have the correct builtin include paths only for Qbs but we are testing on qmake as well. So, expect the BOOST_INCLUDE_DIR set on macOS if we want to have this test executed. Change-Id: Ib9ad697b59bfad5413ad984c9b969ec8b174ae9a Reviewed-by: David Schulz --- src/plugins/autotest/autotestunittests.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/plugins/autotest/autotestunittests.cpp b/src/plugins/autotest/autotestunittests.cpp index eeeeb599de3..614a61a6c27 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; } From 83bf414c85c62de42dfbfd34221ffae6e73e7601 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Wed, 19 Jun 2019 16:11:54 +0200 Subject: [PATCH 26/59] ClangTools: Hint to "Build the project before starting" option ...if the analysis ends with errors. Task-number: QTCREATORBUG-22382 Change-Id: I59f99947e80f7f4d0e2cac4df2e00f68e385ee01 Reviewed-by: Leena Miettinen Reviewed-by: David Schulz --- src/plugins/clangtools/clangtoolruncontrol.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/plugins/clangtools/clangtoolruncontrol.cpp b/src/plugins/clangtools/clangtoolruncontrol.cpp index 2ff19c620e9..cc8dd249714 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(); } From 939d3fe03548af5744fdab1c7f9abf881e43926a Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Mon, 24 Jun 2019 12:44:13 +0200 Subject: [PATCH 27/59] Squish: Update message box "changed on disk" Change-Id: I53149a67e3072a0466ccbb80c55bf9f76b33cb2a Reviewed-by: Christian Stenger --- tests/system/suite_editors/tst_edit_externally/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/system/suite_editors/tst_edit_externally/test.py b/tests/system/suite_editors/tst_edit_externally/test.py index ec6fcd92037..9c2ea61d2cf 100644 --- a/tests/system/suite_editors/tst_edit_externally/test.py +++ b/tests/system/suite_editors/tst_edit_externally/test.py @@ -47,7 +47,8 @@ def main(): mBox = ("{text?='The file * has been changed on disk. Do you want to reload it?' " "type='QMessageBox' unnamed='1' visible='1'}") - popupText = "The file %s has been changed on disk. Do you want to reload it?" + popupText = ("

The file %s has been changed on disk. Do you want to reload it?

" + "

The default behavior can be set in Tools > Options > Environment > System.

") formerContent = None for i, currentFile in enumerate(files): From d1d7a488d95311e6a39f411846e7a3f995c8dab7 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Thu, 13 Jun 2019 16:22:38 +0200 Subject: [PATCH 28/59] Squish: Update GuiAppWizardDialog Change-Id: Icdcab66e4d3a231286607a32ba8ce66d47741442 Reviewed-by: Christian Stenger --- tests/system/objects.map | 28 ++++++++----------- tests/system/shared/project.py | 3 +- .../system/suite_tools/tst_git_clone/test.py | 2 +- 3 files changed, 15 insertions(+), 18 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index 276ae23b357..b1989cbe7ad 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -76,10 +76,10 @@ :FormEditorStack.menuBar_QDesignerMenuBar {container=':*Qt Creator.FormEditorStack_Designer::Internal::FormEditorStack' name='menuBar' type='QDesignerMenuBar' visible='1'} :FormEditorStack_qdesigner_internal::FormWindow {container=':*Qt Creator.FormEditorStack_Designer::Internal::FormEditorStack' type='qdesigner_internal::FormWindow' unnamed='1' visible='1'} :FormEditorStack_qdesigner_internal::PropertyLineEdit {container=':*Qt Creator.FormEditorStack_Designer::Internal::FormEditorStack' type='qdesigner_internal::PropertyLineEdit' unnamed='1' visible='1'} -:Git Repository Clone.Cancel_QPushButton {text='Cancel' type='QPushButton' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:Git Repository Clone.Finish_QPushButton {text~='(Finish|Done)' type='QPushButton' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:Git Repository Clone.Result._QLabel {type='QLabel' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:Git Repository Clone.logPlainTextEdit_QPlainTextEdit {type='QPlainTextEdit' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} +:Git Repository Clone.Cancel_QPushButton {text='Cancel' type='QPushButton' visible='1' window=':New_ProjectExplorer::JsonWizard'} +:Git Repository Clone.Finish_QPushButton {text~='(Finish|Done)' type='QPushButton' visible='1' window=':New_ProjectExplorer::JsonWizard'} +:Git Repository Clone.Result._QLabel {type='QLabel' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} +:Git Repository Clone.logPlainTextEdit_QPlainTextEdit {type='QPlainTextEdit' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} :Go to slot.OK_QPushButton {text='OK' type='QPushButton' unnamed='1' visible='1' window=':Go to slot_QDialog'} :Go to slot.Select signal_QGroupBox {name='groupBox' title='Select signal' type='QGroupBox' visible='1' window=':Go to slot_QDialog'} :Go to slot_QDialog {name='SelectSignalDialog' type='QDialog' visible='1' windowTitle='Go to slot'} @@ -88,14 +88,14 @@ :Hits_QResultWidget {aboveWidget=':Hits_QLabel' type='QResultWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Kits_QtVersion_QComboBox {container=':qt_tabwidget_stackedwidget_QWidget' leftWidget=':QtVersionLabel_KitPage' type='QComboBox' unnamed='1' visible='1'} :Locals and Expressions_Debugger::Internal::WatchTreeView {container=':Debugger.Docks.LocalsAndWatchersDockWidget.Inspector_QFrame' name='WatchWindow' type='Debugger::Internal::WatchTreeView' visible='1'} -:Minimal required Qt version:_QLabel {text='Minimal required Qt version:' type='QLabel' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:New Text File.Add to project:_QLabel {name='projectLabel' text='Add to project:' type='QLabel' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:New Text File.nameLineEdit_Utils::FileNameValidatingLineEdit {name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} -:New Text File_ProjectExplorer::JsonWizard {type='ProjectExplorer::JsonWizard' unnamed='1' visible='1'} +:Minimal required Qt version:_QLabel {text='Minimal required Qt version:' type='QLabel' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} +:New Text File.Add to project:_QLabel {name='projectLabel' text='Add to project:' type='QLabel' visible='1' window=':New_ProjectExplorer::JsonWizard'} +:New Text File.nameLineEdit_Utils::FileNameValidatingLineEdit {name='nameLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :New.comboBox_QComboBox {name='comboBox' type='QComboBox' visible='1' window=':New_Core::Internal::NewDialog'} :New.frame_QFrame {name='frame' type='QFrame' visible='1' window=':New_Core::Internal::NewDialog'} :New.templateCategoryView_QTreeView {name='templateCategoryView' type='QTreeView' visible='1' window=':New_Core::Internal::NewDialog'} :New_Core::Internal::NewDialog {name='Core__Internal__NewDialog' type='Core::Internal::NewDialog' visible='1' windowTitle?='New*'} +:New_ProjectExplorer::JsonWizard {type='ProjectExplorer::JsonWizard' unnamed='1' visible='1'} :Next_QPushButton {text~='(Next.*|Continue)' type='QPushButton' visible='1'} :No valid kits found._QLabel {text?='*No valid kits found.*' type='QLabel' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :OpenDocuments_Widget {type='Core::Internal::OpenEditorsWidget' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow' windowTitle='Open Documents'} @@ -163,10 +163,6 @@ :Qt Creator_Utils::BuildDirectoryLineEdit {name='shadowBuildDirEditLineEdit' type='Utils::FancyLineEdit' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::NavigationTreeView {type='Utils::NavigationTreeView' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :Qt Creator_Utils::NavigationTreeView::QExpandingLineEdit {container=':Qt Creator_Utils::NavigationTreeView' type='QExpandingLineEdit' unnamed='1' visible='1'} -:Qt Gui Application.Form file:_QLabel {name='formLabel' text='Form file:' type='QLabel' visible='1' window=':Qt Gui Application_QmakeProjectManager::Internal::GuiAppWizardDialog'} -:Qt Gui Application.Header file:_QLabel {name='headerLabel' text='Header file:' type='QLabel' visible='1' window=':Qt Gui Application_QmakeProjectManager::Internal::GuiAppWizardDialog'} -:Qt Gui Application.Source file:_QLabel {name='sourceLabel' text='Source file:' type='QLabel' visible='1' window=':Qt Gui Application_QmakeProjectManager::Internal::GuiAppWizardDialog'} -:Qt Gui Application_QmakeProjectManager::Internal::GuiAppWizardDialog {type='QmakeProjectManager::Internal::GuiAppWizardDialog' unnamed='1' visible='1' windowTitle='Qt Widgets Application'} :QtSupport__Internal__QtVersionManager.QLabel {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' text?='Qt version *' type='QLabel' unnamed='1' visible='1'} :QtSupport__Internal__QtVersionManager.errorLabel.QLabel {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' name='errorLabel' type='QLabel' visible='1'} :QtSupport__Internal__QtVersionManager.qmake_QLabel {container=':qt_tabwidget_stackedwidget.QtSupport__Internal__QtVersionManager_QtSupport::Internal::QtOptionsPageWidget' name='qmakePath' type='QLabel' visible='1'} @@ -200,12 +196,12 @@ :Take a UI Tour_Utils::CheckableMessageBox {type='Utils::CheckableMessageBox' unnamed='1' visible='1' windowTitle='Take a UI Tour'} :User Interface.languageBox_QComboBox {container=':Core__Internal__GeneralSettings.User Interface_QGroupBox' name='languageBox' type='QComboBox' visible='1'} :Widget Box_qdesigner_internal::WidgetBoxTreeWidget {container=':*Qt Creator.Widget Box_QDockWidget' type='qdesigner_internal::WidgetBoxTreeWidget' unnamed='1' visible='1'} -:Working Copy_Utils::BaseValidatingLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':New Text File_ProjectExplorer::JsonWizard'} +:Working Copy_Utils::BaseValidatingLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':New_ProjectExplorer::JsonWizard'} :WritePermissions_Core::Internal::ReadOnlyFilesDialog {name='Core__Internal__ReadOnlyFilesDialog' type='Core::ReadOnlyFilesDialog' visible='1' windowTitle='Files Without Write Permissions'} :addToVersionControlComboBox_QComboBox {name='addToVersionControlComboBox' type='QComboBox' visible='1'} -:formFileLineEdit_Utils::FileNameValidatingLineEdit {buddy=':Qt Gui Application.Form file:_QLabel' name='formFileLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1'} +:formFileLineEdit_Utils::FileNameValidatingLineEdit {name='FormFileName' type='Utils::FancyLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :frame.templateDescription_QTextBrowser {container=':New.frame_QFrame' name='templateDescription' type='QTextBrowser' visible='1'} -:headerFileLineEdit_Utils::FileNameValidatingLineEdit {buddy=':Qt Gui Application.Header file:_QLabel' name='headerFileLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1'} +:headerFileLineEdit_Utils::FileNameValidatingLineEdit {name='HdrFileName' type='Utils::FancyLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :popupFrame_Proposal_QListView {container=':popupFrame_TextEditor::GenericProposalWidget' type='QListView' unnamed='1' visible='1'} :popupFrame_TextEditor::GenericProposalWidget {name='m_popupFrame' type='TextEditor::GenericProposalWidget' visible='1'} :projectComboBox_QComboBox {buddy=':New Text File.Add to project:_QLabel' name='projectComboBox' type='QComboBox' visible='1'} @@ -222,7 +218,7 @@ :scrollArea.Edit build configuration:_QLabel {text='Edit build configuration:' type='QLabel' unnamed='1' visible='1'} :scrollArea.Library not available_QLabel {name='qmlDebuggingWarningText' text?='Library not available*' type='QLabel' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :scrollArea.qmlDebuggingLibraryCheckBox_QCheckBox {name='qmlDebuggingLibraryCheckBox' type='QCheckBox' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} -:sourceFileLineEdit_Utils::FileNameValidatingLineEdit {buddy=':Qt Gui Application.Source file:_QLabel' name='sourceFileLineEdit' type='Utils::FileNameValidatingLineEdit' visible='1'} +:sourceFileLineEdit_Utils::FileNameValidatingLineEdit {name='SrcFileName' type='Utils::FancyLineEdit' visible='1' window=':New_ProjectExplorer::JsonWizard'} :splitter.Commit File(s)_VcsBase::QActionPushButton {text~='(Commit .+/.+ File.*)' type='VcsBase::QActionPushButton' unnamed='1' visible='1' window=':Qt Creator_Core::Internal::MainWindow'} :splitter.Description_QGroupBox {container=':Qt Creator.splitter_QSplitter' name='descriptionBox' title='Description' type='QGroupBox' visible='1'} :splitter.Files_QGroupBox {container=':Qt Creator.splitter_QSplitter' name='groupBox' title='Files' type='QGroupBox' visible='1'} diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index a4be7f14d1f..a52c9e5b79b 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -216,7 +216,7 @@ def createProject_Qt_GUI(path, projectName, checks = True, addToVersionControl = template = "Qt Widgets Application" available = __createProjectOrFileSelectType__(" Application", template) __createProjectSetNameAndPath__(path, projectName, checks) - __selectQtVersionDesktop__(checks, available, True) + __handleBuildSystem__(None) if checks: exp_filename = "mainwindow" @@ -234,6 +234,7 @@ def createProject_Qt_GUI(path, projectName, checks = True, addToVersionControl = test.compare(findObject(":formFileLineEdit_Utils::FileNameValidatingLineEdit").text, ui_file) clickButton(waitForObject(":Next_QPushButton")) + __selectQtVersionDesktop__(checks, available, True) expectedFiles = [] if checks: diff --git a/tests/system/suite_tools/tst_git_clone/test.py b/tests/system/suite_tools/tst_git_clone/test.py index dbafe776908..476c1be3652 100644 --- a/tests/system/suite_tools/tst_git_clone/test.py +++ b/tests/system/suite_tools/tst_git_clone/test.py @@ -55,7 +55,7 @@ def verifyCloneLog(targetDir, canceled): if canceled: test.warning("Could not find resultLabel", "Cloning might have failed before clicking 'Cancel'") - return object.exists(":New Text File_ProjectExplorer::JsonWizard") + return object.exists(":New_ProjectExplorer::JsonWizard") else: test.fail("Could not find resultLabel") return True From 07ba637433db227774fdabdc3a8f48e09a9fb3a9 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 25 Jun 2019 08:56:25 +0200 Subject: [PATCH 29/59] Remote Linux: Use double quotes for emphasis in UI text Change-Id: I2195cfe93d3ca77e8ba4d33f73164680ec17c066 Reviewed-by: Christian Kandeler --- src/plugins/remotelinux/makeinstallstep.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/remotelinux/makeinstallstep.cpp b/src/plugins/remotelinux/makeinstallstep.cpp index 0b1fa704add..c9d3578b796 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; From 73f3291c6d3f85a01b476070806f45a81c8a0c99 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 25 Jun 2019 08:54:18 +0200 Subject: [PATCH 30/59] qmake Project Manager: Fix UI text Change-Id: I6048c952ca36d953d7667676435440615d756c45 Reviewed-by: Christian Kandeler --- src/plugins/qmakeprojectmanager/qmakekitinformation.cpp | 2 +- src/plugins/qmakeprojectmanager/qmakesettings.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp b/src/plugins/qmakeprojectmanager/qmakekitinformation.cpp index 623b20a1a4e..95b7c8eb5bb 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 f3c2bf9760e..37c0a4ce39d 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")); From 77f1a5963dca0f06da3975e0e883255d41e885c0 Mon Sep 17 00:00:00 2001 From: Leena Miettinen Date: Tue, 25 Jun 2019 08:48:29 +0200 Subject: [PATCH 31/59] Project Explorer: Fix UI text Use book-style capitalization for dialog and button labels. Change-Id: I2c074d83d6a95ad348daacabd561c758e0a493f8 Reviewed-by: Christian Kandeler --- src/plugins/projectexplorer/environmentwidget.cpp | 2 +- src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp | 4 ++-- src/plugins/projectexplorer/parseissuesdialog.cpp | 8 ++++---- src/plugins/projectexplorer/project.cpp | 4 ++-- src/plugins/projectexplorer/projectexplorer.cpp | 2 +- src/plugins/projectexplorer/projectmodels.cpp | 10 +++++----- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/src/plugins/projectexplorer/environmentwidget.cpp b/src/plugins/projectexplorer/environmentwidget.cpp index d1dcd488fea..1a6e4249600 100644 --- a/src/plugins/projectexplorer/environmentwidget.cpp +++ b/src/plugins/projectexplorer/environmentwidget.cpp @@ -421,7 +421,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/jsonwizard/jsonwizard.cpp b/src/plugins/projectexplorer/jsonwizard/jsonwizard.cpp index 7141148ef8a..7684de07955 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/parseissuesdialog.cpp b/src/plugins/projectexplorer/parseissuesdialog.cpp index 5b6015b5a53..7b0d3758e6e 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 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 16ae9a38dce..d909c4b9090 100644 --- a/src/plugins/projectexplorer/project.cpp +++ b/src/plugins/projectexplorer/project.cpp @@ -661,7 +661,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)); @@ -673,7 +673,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 834fd26b53f..488a9fe1177 100644 --- a/src/plugins/projectexplorer/projectexplorer.cpp +++ b/src/plugins/projectexplorer/projectexplorer.cpp @@ -3422,7 +3422,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 c2c02b1d024..c3df0688ae9 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); From e9e8852f50eabf95a9d8006aee3696ed86d9755d Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 25 Jun 2019 10:03:26 +0200 Subject: [PATCH 32/59] CppTools: Handle not only "-std=X" but also "--std=X" Fixes: QTCREATORBUG-22444 Change-Id: Iedb0b17a26724d0cc8233a3bad273f3e6bd7462d Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- src/plugins/cpptools/compileroptionsbuilder.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index 7d12b33f056..c1cef62c6c3 100644 --- a/src/plugins/cpptools/compileroptionsbuilder.cpp +++ b/src/plugins/cpptools/compileroptionsbuilder.cpp @@ -755,7 +755,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"); From e65aeb5b12348ebd4ebd8c3806372f336a52cb98 Mon Sep 17 00:00:00 2001 From: Cristian Adam Date: Mon, 24 Jun 2019 13:15:29 +0200 Subject: [PATCH 33/59] LanguageServerProtocol: Fix compilation warnings Remove export macro from inline functions. Change-Id: Ifd3cedb88e8da1b043c81f99624556843671aab3 Reviewed-by: David Schulz --- src/libs/languageserverprotocol/icontent.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/libs/languageserverprotocol/icontent.h b/src/libs/languageserverprotocol/icontent.h index af14604739e..1b57165b2e2 100644 --- a/src/libs/languageserverprotocol/icontent.h +++ b/src/libs/languageserverprotocol/icontent.h @@ -92,7 +92,7 @@ using ResponseHandler = std::function; using ResponseHandlers = std::function; using MethodHandler = std::function; -inline LANGUAGESERVERPROTOCOL_EXPORT uint qHash(const LanguageServerProtocol::MessageId &id) +inline uint qHash(const LanguageServerProtocol::MessageId &id) { if (Utils::holds_alternative(id)) return QT_PREPEND_NAMESPACE(qHash(Utils::get(id))); @@ -102,8 +102,7 @@ inline LANGUAGESERVERPROTOCOL_EXPORT uint qHash(const LanguageServerProtocol::Me } template -inline LANGUAGESERVERPROTOCOL_EXPORT QDebug operator<<(QDebug stream, - const LanguageServerProtocol::MessageId &id) +inline QDebug operator<<(QDebug stream, const LanguageServerProtocol::MessageId &id) { if (Utils::holds_alternative(id)) stream << Utils::get(id); From d97e677be477e8f80acf0eff91e761443e129a61 Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Sun, 23 Jun 2019 08:53:19 +0300 Subject: [PATCH 34/59] VCS: Do not use monospace font in output window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Change-Id: Id022a55152880a999c6c6c81ca88f70abfa88b8b Reviewed-by: André Hartmann --- src/plugins/vcsbase/vcsoutputwindow.cpp | 7 ------- 1 file changed, 7 deletions(-) diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index 76ffdf46ea5..07113e7fd9b 100644 --- a/src/plugins/vcsbase/vcsoutputwindow.cpp +++ b/src/plugins/vcsbase/vcsoutputwindow.cpp @@ -291,24 +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(this, &IOutputPane::resetZoom, &d->widget, &Core::OutputWindow::resetZoom); - connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::fontSettingsChanged, - this, updateFontSettings); connect(TextEditor::TextEditorSettings::instance(), &TextEditor::TextEditorSettings::behaviorSettingsChanged, this, updateBehaviorSettings); } From 29e92e4dda540aea4629219314f6841f6ef06353 Mon Sep 17 00:00:00 2001 From: Ulf Hermann Date: Tue, 25 Jun 2019 14:31:27 +0200 Subject: [PATCH 35/59] PerfProfiler: Connect the updateRunActions() signal earlier The startup project can get enabled before we create the views. In that case we miss an update and the run button stays disabled. Fixes: QTCREATORBUG-22616 Change-Id: I5af9075a4899a5dd0e5f69c13348510cde47285d Reviewed-by: hjk --- src/plugins/perfprofiler/perfprofilertool.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/perfprofiler/perfprofilertool.cpp b/src/plugins/perfprofiler/perfprofilertool.cpp index 5af097a9ec0..bb71f925dae 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); From 8d0e57e7dbb436191869363e8929f0061fe90912 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 25 Jun 2019 13:27:06 +0200 Subject: [PATCH 36/59] Squish: Fix tst_qml_locals The locals and expressions display nowadays an additional column which might be hidden. Use the right 'Value' column to fetch the values. Fixes: QTCREATORBUG-22617 Change-Id: I646b05eed607c5941f2713013221eb4c511d901b Reviewed-by: Robert Loehning --- tests/system/suite_debugger/tst_qml_locals/test.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/system/suite_debugger/tst_qml_locals/test.py b/tests/system/suite_debugger/tst_qml_locals/test.py index 0430c306c93..f85a2d3b23d 100644 --- a/tests/system/suite_debugger/tst_qml_locals/test.py +++ b/tests/system/suite_debugger/tst_qml_locals/test.py @@ -122,7 +122,8 @@ def fetchItems(index, valIndex, treeView): tree.setName(name) tree.setValue(value) for row in range(model.rowCount(index)): - tree.addChild(fetchItems(model.index(row, 0, index), model.index(row, 1, index), treeView)) + tree.addChild(fetchItems(model.index(row, 0, index), + model.index(row, 2, index), treeView)) return tree def checkForEmptyRows(items, isRootCheck=True): From 8181363fa90eb651591bf71e1a840e1c998429f4 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Mon, 24 Jun 2019 20:39:35 +0200 Subject: [PATCH 37/59] Add changes file for 4.9.2 Change-Id: Icce93b9daed51603093cf1efe8c904253984351b Reviewed-by: Leena Miettinen --- dist/changes-4.9.2.md | 56 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 dist/changes-4.9.2.md diff --git a/dist/changes-4.9.2.md b/dist/changes-4.9.2.md new file mode 100644 index 00000000000..bc998608188 --- /dev/null +++ b/dist/changes-4.9.2.md @@ -0,0 +1,56 @@ +# Qt Creator 4.9.2 + +Qt Creator version 4.9.2 contains bug fixes. + +The most important changes are listed in this document. For a complete +list of changes, see the Git log for the Qt Creator sources that +you can check out from the public Git repository. For example: + + git clone git://code.qt.io/qt-creator/qt-creator.git + git log --cherry-pick --pretty=oneline origin/v4.9.1..v4.9.2 + +## General + +* Fixed display of shortcuts in `Keyboard` preferences (QTCREATORBUG-22333) + +## Editing + +* Fixed disabled editor close button in Design mode (QTCREATORBUG-22553) + +### Syntax Highlighting + +* Fixed highlighting issue while editing (QTCREATORBUG-22290) + +## All Projects + +* Fixed saving state of `Hide Empty Directories` +* Fixed crash that could happen after project parsing failed + +## C++ Support + +* Fixed expansion of `%DATE%` in license templates (QTCREATORBUG-22440) + +## Qt Quick Designer + +* Fixed crash on malformed QML (QDS-778) + +## Platform Specific + +### macOS + +* Re-enabled graphics card switching that was disabled as a workaround + for OpenGL issues on macOS 10.14.4 (QTCREATORBUG-22215) + +## Credits for these changes go to: + +Christian Kandeler +Christian Stenger +David Schulz +Eike Ziller +Leena Miettinen +Michl Voznesensky +Robert Löhning +Thomas Hartmann +Tim Jenssen +Tobias Hunger +Ulf Hermann From 2e528917f061261e4e5a056c90e71c6186344731 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 25 Jun 2019 12:48:09 +0200 Subject: [PATCH 38/59] CppEditor: Add another sanity check to ParseContextModel Task-number: QTCREATORBUG-22596 Change-Id: I222656503477ea8dbd3b65801d1816b77baa7c39 Reviewed-by: David Schulz --- src/plugins/cppeditor/cppparsecontext.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/plugins/cppeditor/cppparsecontext.cpp b/src/plugins/cppeditor/cppparsecontext.cpp index cf77ffe8088..521cc493284 100644 --- a/src/plugins/cppeditor/cppparsecontext.cpp +++ b/src/plugins/cppeditor/cppparsecontext.cpp @@ -111,8 +111,10 @@ 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(); } From c54ef80a4e44f630e01fac84e182dbdaf204131a Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 25 Jun 2019 16:28:27 +0200 Subject: [PATCH 39/59] CppTools: Ignore -f[no-]keep-inline-dllexport Fixes: QTCREATORBUG-22452 Change-Id: Ic17e6331e92f23c31f4f7319257f2d09c66af8a4 Reviewed-by: Cristian Adam Reviewed-by: David Schulz --- src/plugins/cpptools/compileroptionsbuilder.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plugins/cpptools/compileroptionsbuilder.cpp b/src/plugins/cpptools/compileroptionsbuilder.cpp index c1cef62c6c3..3d74ad8d326 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 @@ -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)) { From 38685de8942d35e7fe24cc3b32dfab85392eeac0 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Mon, 24 Jun 2019 14:14:24 +0200 Subject: [PATCH 40/59] Squish: Update openCmakeProject Change-Id: I17f1a271eafbcff24987fbd7e31dc6cc8c789b84 Reviewed-by: Christian Stenger --- tests/system/shared/project.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/system/shared/project.py b/tests/system/shared/project.py index a52c9e5b79b..a0eea63b82a 100644 --- a/tests/system/shared/project.py +++ b/tests/system/shared/project.py @@ -53,17 +53,17 @@ def openQmakeProject(projectPath, targets=Targets.desktopTargetClasses(), fromWe def openCmakeProject(projectPath, buildDir): def additionalFunction(): - pChooser = waitForObject("{leftWidget={text='Default' type='QCheckBox' unnamed='1' " + pChooser = waitForObject("{leftWidget={text='Debug' type='QCheckBox' unnamed='1' " "visible='1'} type='Utils::PathChooser' unnamed='1' visible='1'}") lineEdit = getChildByClass(pChooser, "Utils::FancyLineEdit") replaceEditorContent(lineEdit, buildDir) - # disable all build configurations except "Default" - configs = ['Debug', 'Release', 'Release with Debug Information', 'Minimum Size Release'] + # disable all build configurations except "Debug" + configs = ['Release', 'Release with Debug Information', 'Minimum Size Release'] for checkbox in configs: ensureChecked(waitForObject("{text='%s' type='QCheckBox' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}" % checkbox), False) - ensureChecked(waitForObject("{text='Default' type='QCheckBox' unnamed='1' visible='1' " + ensureChecked(waitForObject("{text='Debug' type='QCheckBox' unnamed='1' visible='1' " "window=':Qt Creator_Core::Internal::MainWindow'}"), True) invokeMenuItem("File", "Open File or Project...") From 0313cdbd87a6726194ec25ddff707a6b74389c88 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Fri, 21 Jun 2019 09:18:14 +0200 Subject: [PATCH 41/59] gdbbridge: Convert children to gdb.Value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 'Dumper::fromNativeValue' expects an object of type 'gdb.Value'. However, the 'pretty_printer.children()' iterator may return values that first need to be converted to this, as documented for function 'pretty_printer.children' at [1]: > This method must return an object conforming to the Python iterator > protocol. Each item returned by the iterator must be a tuple holding two > elements. The first element is the “name” of the child; the second > element is the child’s value. The value can be any Python object which > is convertible to a GDB value. Therefore, explicitly convert the value to a GDB value first. This fixes the expansion of 'std::vector' when system GDB pretty printers are enabled which previously led to "" being shown e.g. for the following example (expand 'v' in the local variable view at the breakpoint): #include int main() { std::vector v; v.push_back(true); return 0; // insert breakpoint here } Side note: GCC's pretty printer for 'std::vector' previously returned either '0' or '1' for the element values, thus leading to the problem described above. With this patch in place, the elements are shown when the vector is expanded, but the shown type is 'long long' (since that's the type that GDB seems to automatically assign when constructing a 'gdb.Value' from these integers, at least with GDB 8.2.1 on amd64). This will work as expected ('bool' shown as type) from GCC commit [2] on ("Have std::vector printer's iterator return bool for vector"). [1] https://sourceware.org/gdb/onlinedocs/gdb/Pretty-Printing-API.html [2] https://gcc.gnu.org/git/?p=gcc.git;a=commitdiff;h=6c7d761a3f5fd7d19795d1d4b9b027a04b3fe88b Change-Id: I9047affa5b4369befd2e2386c8a6b04c66c4b632 Reviewed-by: hjk --- share/qtcreator/debugger/gdbbridge.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/debugger/gdbbridge.py b/share/qtcreator/debugger/gdbbridge.py index fdfb39746e4..2f056c7a932 100644 --- a/share/qtcreator/debugger/gdbbridge.py +++ b/share/qtcreator/debugger/gdbbridge.py @@ -153,7 +153,7 @@ class PlainDumper: if d.isExpanded(): with Children(d): for child in children: - d.putSubItem(child[0], d.fromNativeValue(child[1])) + d.putSubItem(child[0], d.fromNativeValue(gdb.Value(child[1]))) def importPlainDumpers(args): if args == 'off': From 01f26bd5b748a7b2257debd0f6d35fb15fcaa284 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Fri, 21 Jun 2019 09:44:53 +0200 Subject: [PATCH 42/59] Fix std::vector printer with custom allocator This fixes the std::vector pretty printer, which previously just showed "" for variable 'v' for the following sample code (with system GDB pretty printer disabled so that the custom pretty printers are used): #include template class myallocator : public std::allocator { }; int main() { std::vector> v; v.push_back(true); return 0; // break here and check value of 'v' } Change-Id: Ia9883aa0b06a396cb3546ac2594a82c1b2062b80 Reviewed-by: hjk --- share/qtcreator/debugger/stdtypes.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index a14029fdc34..8e80aafcdbf 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -973,9 +973,9 @@ def qdumpHelper__std__vector(d, value, isLibCpp): (start, soffset, pad, finish, foffset, pad, alloc) = value.split("pI@pI@p") else: start = value["_M_start"]["_M_p"].pointer() - soffset = value["_M_start"]["_M_offset"] + soffset = value["_M_start"]["_M_offset"].integer() finish = value["_M_finish"]["_M_p"].pointer() - foffset = value["_M_finish"]["_M_offset"] + foffset = value["_M_finish"]["_M_offset"].integer() alloc = value["_M_end_of_storage"].pointer() size = (finish - start) * 8 + foffset - soffset # 8 is CHAR_BIT. else: From 5eba3bde9369fa084f2fe4d935f302ea2061db83 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Fri, 21 Jun 2019 09:57:29 +0200 Subject: [PATCH 43/59] Fix std::basic_string printer with custom allocator This fixes expansion of 'std::basic_string' in the locals view when a custom allocator is used (which previously would result in "" being shown); for example, when expanding 's' at the breakpoint in the following example: #include template class myallocator : public std::allocator {}; int main() { std::basic_string, myallocator> s("hello"); return 0; // break here and expand value of 's' in locals view } Change-Id: I0ca98de50d83a1f6e6f019acc37a1302a05fdba8 Reviewed-by: hjk --- share/qtcreator/debugger/stdtypes.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index 8e80aafcdbf..b0b68995637 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -1095,7 +1095,7 @@ def qdump__std____cxx11__basic_string(d, value): (data, size) = value.split("pI") else: try: - data = value["_M_dataplus"]["_M_p"] + data = value["_M_dataplus"]["_M_p"].pointer() size = int(value["_M_string_length"]) except: d.putEmptyValue() From f223c094a18c21a70894e66f83a7efb165b838ac Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 25 Jun 2019 11:10:00 +0200 Subject: [PATCH 44/59] ClangTools: Show hint when disabling "Build the project before analysis." Show also the same hint as a tooltip. Fixes: QTCREATORBUG-22382 Change-Id: If1b594994cea387d6727775ce4c28c21d51f2d86 Reviewed-by: Leena Miettinen Reviewed-by: David Schulz --- .../clangtools/clangselectablefilesdialog.cpp | 3 +++ .../clangtools/clangtoolsconfigwidget.cpp | 3 +++ src/plugins/clangtools/clangtoolsutils.cpp | 23 ++++++++++++++++++- src/plugins/clangtools/clangtoolsutils.h | 3 +++ 4 files changed, 31 insertions(+), 1 deletion(-) diff --git a/src/plugins/clangtools/clangselectablefilesdialog.cpp b/src/plugins/clangtools/clangselectablefilesdialog.cpp index e48679aa059..c6ad76ff0a1 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/clangtoolsconfigwidget.cpp b/src/plugins/clangtools/clangtoolsconfigwidget.cpp index ad966ab5180..b634875f979 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/clangtoolsutils.cpp b/src/plugins/clangtools/clangtoolsutils.cpp index 3e811500ac9..173d97e31dc 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 -#include +#include #include +#include #include #include @@ -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.

" + "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 6d3a7a90caf..05acb953c5a 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 From 98d7b502caf8d0976a116c5d798be00cfb95e373 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 6 May 2019 15:37:55 +0200 Subject: [PATCH 45/59] ClangRefactoring: Clear input files after collecting symbols Change-Id: Ic4fbaac7ad3b3f80223d6cbb84a34dffa741fc4f Reviewed-by: Tim Jenssen --- .../source/symbolscollector.cpp | 6 ++++- .../source/symbolscollector.h | 2 ++ tests/unit/unittest/symbolscollector-test.cpp | 23 +++++++++++++++---- 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/src/tools/clangrefactoringbackend/source/symbolscollector.cpp b/src/tools/clangrefactoringbackend/source/symbolscollector.cpp index 071837ac528..ab5a00fc6b0 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 cc7ca4396c9..e1034713726 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/tests/unit/unittest/symbolscollector-test.cpp b/tests/unit/unittest/symbolscollector-test.cpp index f0080a5d2c3..a766b70fa40 100644 --- a/tests/unit/unittest/symbolscollector-test.cpp +++ b/tests/unit/unittest/symbolscollector-test.cpp @@ -53,17 +53,18 @@ using testing::Value; using testing::_; using ClangBackEnd::FilePath; -using ClangBackEnd::FilePathId; using ClangBackEnd::FilePathCaching; -using ClangBackEnd::V2::FileContainers; +using ClangBackEnd::FilePathId; +using ClangBackEnd::FileStatus; using ClangBackEnd::SourceDependency; using ClangBackEnd::SourceLocationEntry; +using ClangBackEnd::SourceLocationKind; using ClangBackEnd::SymbolEntry; +using ClangBackEnd::SymbolIndex; using ClangBackEnd::SymbolKind; using ClangBackEnd::SymbolTag; -using ClangBackEnd::SourceLocationKind; -using ClangBackEnd::SymbolIndex; using ClangBackEnd::UsedMacro; +using ClangBackEnd::V2::FileContainers; using Sqlite::Database; @@ -130,6 +131,9 @@ MATCHER_P(HasSymbolTag, symbolTag, class SymbolsCollector : public testing::Test { protected: + SymbolsCollector() { setFilePathCache(&filePathCache); } + ~SymbolsCollector() { setFilePathCache({}); } + FilePathId filePathId(Utils::SmallStringView filePath) const { return filePathCache.filePathId(ClangBackEnd::FilePathView{filePath}); @@ -664,4 +668,15 @@ TEST_F(SymbolsCollector, CollectReturnsFalseIfThereIsNoError) ASSERT_TRUE(success); } + +TEST_F(SymbolsCollector, ClearInputFilesAfterCollectingSymbols) +{ + collector.setFile(filePathId(TESTDATA_DIR "/symbolscollector/main2.cpp"), {"cc"}); + collector.collectSymbols(); + collector.setFile(filePathId(TESTDATA_DIR "/symbolscollector/main.cpp"), {"cc"}); + + collector.collectSymbols(); + + ASSERT_TRUE(collector.isClean()); +} } // namespace From aedc0ca91bd1bc0ad9a3e411f8c9996250e14d61 Mon Sep 17 00:00:00 2001 From: Michael Weghorn Date: Fri, 21 Jun 2019 10:25:23 +0200 Subject: [PATCH 46/59] Pretty printers: Unify code for different allocators The code path for allocators other than 'std::allocator' does work for 'std::allocator' as well, so unify this. This also fixes the case of std containers when 'std::allocator' is used and the compiler flag '-D_GLIBCXX_DEBUG' in place which results in size assumptions that were made in the now dropped path to not be fulfilled, thus leading to an incorrect display. Fixes: QTCREATORBUG-22606 Change-Id: I2b6f8ac9933b210d26197975017292e2fc227541 Reviewed-by: hjk --- share/qtcreator/debugger/stdtypes.py | 61 +++++++++------------------- 1 file changed, 19 insertions(+), 42 deletions(-) diff --git a/share/qtcreator/debugger/stdtypes.py b/share/qtcreator/debugger/stdtypes.py index b0b68995637..3dfbe5bd8b5 100644 --- a/share/qtcreator/debugger/stdtypes.py +++ b/share/qtcreator/debugger/stdtypes.py @@ -953,43 +953,27 @@ def qdumpHelper__std__vector(d, value, isLibCpp): innerType = value.type[0] isBool = innerType.name == 'bool' - try: - allocator = value.type[1].name - except: - allocator = '' - - isStdAllocator = allocator == 'std::allocator<%s>' % innerType.name - if isBool: if isLibCpp: - if isStdAllocator: - (start, size) = value.split("pp") # start is 'unsigned long *' - else: - start = value["__begin_"].pointer() - size = value["__size_"] + start = value["__begin_"].pointer() + size = value["__size_"] alloc = size else: - if isStdAllocator: - (start, soffset, pad, finish, foffset, pad, alloc) = value.split("pI@pI@p") - else: - start = value["_M_start"]["_M_p"].pointer() - soffset = value["_M_start"]["_M_offset"].integer() - finish = value["_M_finish"]["_M_p"].pointer() - foffset = value["_M_finish"]["_M_offset"].integer() - alloc = value["_M_end_of_storage"].pointer() + start = value["_M_start"]["_M_p"].pointer() + soffset = value["_M_start"]["_M_offset"].integer() + finish = value["_M_finish"]["_M_p"].pointer() + foffset = value["_M_finish"]["_M_offset"].integer() + alloc = value["_M_end_of_storage"].pointer() size = (finish - start) * 8 + foffset - soffset # 8 is CHAR_BIT. else: - if isStdAllocator: - (start, finish, alloc) = value.split("ppp") + if isLibCpp: + start = value["__begin_"].pointer() + finish = value["__end_"].pointer() + alloc = value["__end_cap_"].pointer() else: - if isLibCpp: - start = value["__begin_"].pointer() - finish = value["__end_"].pointer() - alloc = value["__end_cap_"].pointer() - else: - start = value["_M_start"].pointer() - finish = value["_M_finish"].pointer() - alloc = value["_M_end_of_storage"].pointer() + start = value["_M_start"].pointer() + finish = value["_M_finish"].pointer() + alloc = value["_M_end_of_storage"].pointer() size = int((finish - start) / innerType.size()) d.check(finish <= alloc) if size > 0: @@ -1088,19 +1072,12 @@ def qdump__std__basic_string(d, value): def qdump__std____cxx11__basic_string(d, value): innerType = value.type[0] try: - allocator = value.type[2].name + data = value["_M_dataplus"]["_M_p"].pointer() + size = int(value["_M_string_length"]) except: - allocator = '' - if allocator == 'std::allocator<%s>' % innerType.name: - (data, size) = value.split("pI") - else: - try: - data = value["_M_dataplus"]["_M_p"].pointer() - size = int(value["_M_string_length"]) - except: - d.putEmptyValue() - d.putPlainChildren(value) - return + d.putEmptyValue() + d.putPlainChildren(value) + return d.check(0 <= size) #and size <= alloc and alloc <= 100*1000*1000) d.putCharArrayHelper(data, size, innerType, d.currentItemFormat()) From 81ee28fc982dafde00c53f499544312d5deb6c9a Mon Sep 17 00:00:00 2001 From: Orgad Shaneh Date: Wed, 26 Jun 2019 11:16:49 +0300 Subject: [PATCH 47/59] VCS: Add seconds to timestamp in output window MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Useful when there are multiple commands at the same minute. Change-Id: Ie5c344fe3d19e54e5b2fce1a5b3dc0cfdeb267e2 Reviewed-by: André Hartmann --- src/plugins/vcsbase/vcsoutputwindow.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/plugins/vcsbase/vcsoutputwindow.cpp b/src/plugins/vcsbase/vcsoutputwindow.cpp index 07113e7fd9b..f27715ed0c0 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); From dcb03c115e56ac5f31f614a0cec4276966d248fe Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 26 Jun 2019 14:31:36 +0200 Subject: [PATCH 48/59] Add changes for 4.10 beta2 Change-Id: Ie399b7445f31bb5e597df7ab81582ecb9ea83ad7 Reviewed-by: Leena Miettinen --- dist/changes-4.10.0.md | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/dist/changes-4.10.0.md b/dist/changes-4.10.0.md index baece64d4f4..651c719a328 100644 --- a/dist/changes-4.10.0.md +++ b/dist/changes-4.10.0.md @@ -13,6 +13,12 @@ you can check out from the public Git repository. For example: * Removed support for KDE code paster after removal of official API * Added option for pinning files so they stay open when closing all files (QTCREATORBUG-21899) +* Fixed low contrast of hovered folding markers (QTCREATORBUG-21702) + +### Generic Highlighter + +* Fixed that highlighting definition with MIME type `text/plain` + overrode better matching definitions (QTCREATORBUG-22540) ### Language Client @@ -96,6 +102,9 @@ you can check out from the public Git repository. For example: * Improved auto-insertion of closing curly brace (QTCREATORBUG-18872) * Fixed that snippet completion could get in the way (QTCREATORBUG-21767) +* Fixed crash because of small stack size (QTCREATORBUG-22496) +* Fixed recognition of C++ version (QTCREATORBUG-22444) +* Fixed `unknown argument: '-fno-keep-inline-dllexport'` (QTCREATORBUG-22452) ### Clang Format @@ -105,6 +114,8 @@ you can check out from the public Git repository. For example: ## QML Support * Fixed various formatting issues +* Fixed incorrect syntax warning in JavaScript template literal + (QTCREATORBUG-22474) ## Debugging @@ -127,6 +138,7 @@ you can check out from the public Git repository. For example: * Added gradient picker that allows loading and saving of presets * Added support for changing properties for multiple items at once (QDS-324) * Added missing properties for `LineEdit` and `ComboBox` +* Added all fonts from project directory to font selector (QDS-100) * Updated properties of `Flickable` * Improved handling of errors in state editor (QDS-695) @@ -154,13 +166,12 @@ you can check out from the public Git repository. For example: * Added `Clone` for MSVC toolchains (QTCREATORBUG-22163) * Fixed that `mingw32-make`'s warnings were categorized as errors (QTCREATORBUG-22171) * Fixed bitness detection for MinGW (QTCREATORBUG-22160) +* Fixed registration as post mortem debugger on recent Windows versions ### Linux * Improved auto-detection of toolchains (QTCREATORBUG-19179, QTCREATORBUG-20044, QTCREATORBUG-22081) -### macOS - ### Android * Removed support for MIPS64 @@ -172,13 +183,12 @@ you can check out from the public Git repository. For example: * Added support for opening remote terminal with run environment * Added option for `rsync` flags for deployment (QTCREATORBUG-22352) -### Boot to Qt - ### Bare Metal * Added include path detection and output parsers for `IAR`, `KEIL` and `SDCC` toolchains ## Credits for these changes go to: + Aleksei German Alessandro Ambrosano Alessandro Portale @@ -208,10 +218,12 @@ Ivan Komissarov Joel Smith Jörg Bornemann Kavindra Palaraja +Knud Dollereder Leena Miettinen Luca Carlon Marco Bubke Martin Haase +Michael Weghorn Mitch Curtis Nikolai Kosjar Oliver Wolff From 33d49ec07248b729932632dd6b7c2336a77e5839 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Wed, 26 Jun 2019 14:41:37 +0200 Subject: [PATCH 49/59] Fix lupdate issues Change-Id: I0a7cc0650b6427d56b4415b2fff6cf39e29c96f9 Reviewed-by: Leena Miettinen Reviewed-by: Christian Stenger --- .../autotest/boost/boosttestoutputreader.h | 1 + .../filterkitaspectsdialog.cpp | 2 +- .../projectexplorer/filterkitaspectsdialog.h | 1 + src/plugins/valgrind/memchecktool.cpp | 46 +++++++++++-------- 4 files changed, 31 insertions(+), 19 deletions(-) diff --git a/src/plugins/autotest/boost/boosttestoutputreader.h b/src/plugins/autotest/boost/boosttestoutputreader.h index e16c1fb9963..91c38cb62fe 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 &futureInterface, QProcess *testApplication, const QString &buildDirectory, diff --git a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp index 418ae035642..12bd7ccf13c 100644 --- a/src/plugins/projectexplorer/filterkitaspectsdialog.cpp +++ b/src/plugins/projectexplorer/filterkitaspectsdialog.cpp @@ -92,7 +92,7 @@ class FilterKitAspectsModel : public TreeModel 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 441abd658d6..f59a58f683f 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 irrelevantAspects() const; diff --git a/src/plugins/valgrind/memchecktool.cpp b/src/plugins/valgrind/memchecktool.cpp index fee5c9a8d4d..d83cb16afa4 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("Heob")); + QMessageBox::critical( + Core::ICore::mainWindow(), + MemcheckTool::tr("Heob"), + MemcheckTool::tr("The %1 executables must be in the appropriate location.") + .arg("Heob")); 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("Dwarfstack"), - 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( + "Dwarfstack"), + 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(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; From 6288e03264129ff6e57211bf86d7ff7b98d95726 Mon Sep 17 00:00:00 2001 From: Christian Stenger Date: Tue, 25 Jun 2019 09:27:31 +0200 Subject: [PATCH 50/59] Wizards: Fix regular expressions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Although this is valid JavaScript matching the '.' is not working as expected. The directory always gets messed up by replacing the first character with a dash. Use an alternate regular expression and also ensure the replacement happens globally instead of once as we want to replace all dots by a dash. As at it fix handling of the file suffixes as well. Task-number: QTCREATORBUG-22625 Change-Id: Ide189fe50e29994abfb368e503050b9b413aba9f Reviewed-by: André Hartmann Reviewed-by: Eike Ziller --- share/qtcreator/templates/wizards/files/java/wizard.json | 2 +- .../templates/wizards/projects/vcs/bazaar/wizard.json | 2 +- .../qtcreator/templates/wizards/projects/vcs/git/wizard.json | 4 ++-- .../templates/wizards/projects/vcs/mercurial/wizard.json | 2 +- .../templates/wizards/projects/vcs/subversion/wizard.json | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/share/qtcreator/templates/wizards/files/java/wizard.json b/share/qtcreator/templates/wizards/files/java/wizard.json index 6341319c6ba..5a8fae5dc16 100644 --- a/share/qtcreator/templates/wizards/files/java/wizard.json +++ b/share/qtcreator/templates/wizards/files/java/wizard.json @@ -9,7 +9,7 @@ "iconText": "java", "enabled": "%{JS: value('Plugins').indexOf('Android') >= 0}", - "options": [ { "key": "ClassName", "value": "%{JS: value('FileName').charAt(0).toUpperCase() + value('FileName').substr(1).replace(/\\.java$/,'')}" } ], + "options": [ { "key": "ClassName", "value": "%{JS: value('FileName').charAt(0).toUpperCase() + value('FileName').substr(1).replace(/[.]java$/,'')}" } ], "pages" : [ diff --git a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json index a8b4d041e71..f81509c4ed8 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/bazaar/wizard.json @@ -14,7 +14,7 @@ { "key": "vcsId", "value": "B.Bazaar" }, { "key": "vcsName", "value": "%{JS: Vcs.displayName('%{vcsId}')}" }, { "key": "SR", "value": "%{JS: '%{Repo}'.substr('%{Repo}'.indexOf(':') + 1) }" }, - { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/\\./, '-') }"}, + { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/[.]/g, '-') }"}, { "key": "RevArg", "value": "%{JS: '%{Rev}' !== '' ? '-r' : ''}" }, { "key": "TargetPath", "value": "%{Path}/%{Dir}" } ], diff --git a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json index c97698612b6..0295468f6fb 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/git/wizard.json @@ -13,8 +13,8 @@ [ { "key": "vcsId", "value": "G.Git" }, { "key": "vcsName", "value": "%{JS: Vcs.displayName('%{vcsId}')}" }, - { "key": "SR", "value": "%{JS: '%{Repo}'.replace(/\\.git$/, '') }"}, - { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/\\./, '-') }"}, + { "key": "SR", "value": "%{JS: '%{Repo}'.replace(/[.]git$/, '') }"}, + { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/[.]/g, '-') }"}, { "key": "branchArg", "value": "%{JS: '%{Branch}' ? '--branch' : '' }" }, { "key": "TargetPath", "value": "%{Path}/%{Dir}" } ], diff --git a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json index 75bd410861a..8e655e1eb66 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/mercurial/wizard.json @@ -14,7 +14,7 @@ { "key": "vcsId", "value": "H.Mercurial" }, { "key": "vcsName", "value": "%{JS: Vcs.displayName('%{vcsId}')}" }, { "key": "SR", "value": "%{JS: '%{Repo}'.substr('%{Repo}'.indexOf(':') + 1) }"}, - { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/\\./, '-') }"}, + { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/[.]/g, '-') }"}, { "key": "TargetPath", "value": "%{Path}/%{Dir}" } ], diff --git a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json index aef8a28b9ac..cfeddbb5327 100644 --- a/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json +++ b/share/qtcreator/templates/wizards/projects/vcs/subversion/wizard.json @@ -13,8 +13,8 @@ [ { "key": "vcsId", "value": "J.Subversion" }, { "key": "vcsName", "value": "%{JS: Vcs.displayName('%{vcsId}')}" }, - { "key": "SR", "value": "%{JS: '%{Repo}'.replace(/\\/trunk$/, '').replace(/\\/$/, '') }"}, - { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/\\./, '-') }"}, + { "key": "SR", "value": "%{JS: '%{Repo}'.replace(/[/]trunk$/, '').replace(/[/]$/, '') }"}, + { "key": "defaultDir", "value": "%{JS: '%{SR}'.substr('%{SR}'.lastIndexOf('/') + 1).replace(/[.]/g, '-') }"}, { "key": "TargetPath", "value": "%{Path}/%{Dir}" } ], From 375ec4c51b0732d8f91d451590ab59156a8f1c84 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 26 Jun 2019 16:38:41 +0200 Subject: [PATCH 51/59] Wizards: Fix qbs project template for QtQuick app Change-Id: Iffc9dad352d78f54557f45b36687c543935853dd Reviewed-by: Christian Stenger --- .../templates/wizards/projects/qtquickapplication/app.qbs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/share/qtcreator/templates/wizards/projects/qtquickapplication/app.qbs b/share/qtcreator/templates/wizards/projects/qtquickapplication/app.qbs index d86ee9b4549..27a8b66c3c7 100644 --- a/share/qtcreator/templates/wizards/projects/qtquickapplication/app.qbs +++ b/share/qtcreator/templates/wizards/projects/qtquickapplication/app.qbs @@ -2,7 +2,7 @@ import qbs Application { @if "%{UseVirtualKeyboard}" == "true" - Depends { name: "Qt"; submodules: "quick", "virtualkeyboard" } + Depends { name: "Qt"; submodules: ["quick", "virtualkeyboard"] } @else Depends { name: "Qt.quick" } @endif From 9d290fc68206ce8323c259aab099faba93f7ec83 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 26 Jun 2019 15:59:25 +0200 Subject: [PATCH 52/59] Fix build with mingw 5.3 Change-Id: I81609bb475ca1de73acbb3fb60b76b40ff8a39a0 Reviewed-by: Christian Stenger --- src/plugins/baremetal/iarewtoolchain.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/plugins/baremetal/iarewtoolchain.cpp b/src/plugins/baremetal/iarewtoolchain.cpp index 1955a0fec78..70fd42fbccd 100644 --- a/src/plugins/baremetal/iarewtoolchain.cpp +++ b/src/plugins/baremetal/iarewtoolchain.cpp @@ -417,9 +417,9 @@ QList IarToolChainFactory::autoDetect(const QList &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); From bbd58ca30b32f6015a5e7eb16884dfb9f6108f17 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Thu, 6 Jun 2019 10:57:55 +0200 Subject: [PATCH 53/59] Clang: Watch directories instead of files Because there a limited resources to watch files we watch now directories. So we need much less resources. Change-Id: Iac558832e9521a7a1a67c5ea99b42ad1b0b5129c Reviewed-by: Tim Jenssen --- src/libs/clangsupport/clangpathwatcher.h | 218 ++++++++------ src/libs/clangsupport/clangsupport-lib.pri | 10 +- .../clangsupport/directoryandfilepathid.h | 90 ++++++ ...compressor.h => directorypathcompressor.h} | 52 ++-- src/libs/clangsupport/directorypathid.h | 81 +++++ src/libs/clangsupport/filepathcache.h | 48 ++- src/libs/clangsupport/filepathcaching.cpp | 15 + src/libs/clangsupport/filepathcaching.h | 3 + .../clangsupport/filepathcachinginterface.h | 6 +- src/libs/clangsupport/filepathexceptions.h | 9 + src/libs/clangsupport/filepathstorage.h | 22 +- .../filepathstoragesqlitestatementfactory.h | 2 + .../source => libs/clangsupport}/filestatus.h | 0 src/libs/clangsupport/filestatuscache.cpp | 169 +++++++++++ .../clangsupport}/filestatuscache.h | 36 ++- src/libs/clangsupport/filesystem.cpp | 61 ++++ src/libs/clangsupport/filesystem.h | 48 +++ src/libs/clangsupport/filesysteminterface.h | 43 +++ src/libs/utils/smallstringview.h | 4 + .../clangpchmanagerbackendmain.cpp | 4 +- .../source/clangrefactoringbackend-source.pri | 5 +- .../source/filestatuscache.cpp | 93 ------ .../source/symbolindexing.h | 6 +- .../changedfilepathcompressor-test.cpp | 112 ------- tests/unit/unittest/clangpathwatcher-test.cpp | 285 +++++++++--------- .../unittest/directorypathcompressor-test.cpp | 99 ++++++ tests/unit/unittest/filepathcache-test.cpp | 150 +++++++++ tests/unit/unittest/filepathstorage-test.cpp | 82 ++++- ...pathstoragesqlitestatementfactory-test.cpp | 8 +- tests/unit/unittest/filestatuscache-test.cpp | 180 +++++++++-- .../unit/unittest/gtest-creator-printing.cpp | 2 +- tests/unit/unittest/mockfilepathcaching.h | 6 + tests/unit/unittest/mockfilesystem.h | 37 +++ tests/unit/unittest/mockqfilesystemwatcher.h | 1 + .../unit/unittest/mocksqlitereadstatement.cpp | 6 + tests/unit/unittest/mocksqlitereadstatement.h | 11 +- tests/unit/unittest/symbolindexer-test.cpp | 17 +- tests/unit/unittest/unittest.pro | 5 +- 38 files changed, 1485 insertions(+), 541 deletions(-) create mode 100644 src/libs/clangsupport/directoryandfilepathid.h rename src/libs/clangsupport/{changedfilepathcompressor.h => directorypathcompressor.h} (58%) create mode 100644 src/libs/clangsupport/directorypathid.h rename src/{tools/clangrefactoringbackend/source => libs/clangsupport}/filestatus.h (100%) create mode 100644 src/libs/clangsupport/filestatuscache.cpp rename src/{tools/clangrefactoringbackend/source => libs/clangsupport}/filestatuscache.h (69%) create mode 100644 src/libs/clangsupport/filesystem.cpp create mode 100644 src/libs/clangsupport/filesystem.h create mode 100644 src/libs/clangsupport/filesysteminterface.h delete mode 100644 src/tools/clangrefactoringbackend/source/filestatuscache.cpp delete mode 100644 tests/unit/unittest/changedfilepathcompressor-test.cpp create mode 100644 tests/unit/unittest/directorypathcompressor-test.cpp create mode 100644 tests/unit/unittest/mockfilesystem.h diff --git a/src/libs/clangsupport/clangpathwatcher.h b/src/libs/clangsupport/clangpathwatcher.h index dab52996c28..7a7d6ccfcf3 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 + #include namespace ClangBackEnd { +template +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; -template +template 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) 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() - { - return m_fileSystemWatcher; - } + FileSystemWatcher &fileSystemWatcher() { return m_fileSystemWatcher; } - QStringList convertWatcherEntriesToQStringList( - const WatcherEntries &watcherEntries) + QStringList convertWatcherEntriesToDirectoryPathList(const DirectoryPathIds &directoryPathIds) const { - QStringList paths; - paths.reserve(int(watcherEntries.size())); - - std::transform(watcherEntries.begin(), - watcherEntries.end(), - std::back_inserter(paths), - [&] (WatcherEntry entry) { - return QString(m_pathCache.filePath(entry.pathId).path()); + return Utils::transform(directoryPathIds, [&](DirectoryPathId id) { + return QString(m_pathCache.directoryPath(id)); }); - - return paths; } - template - WatcherEntries notWatchedEntries(const WatcherEntries &entries, - Compare compare) const + QStringList convertWatcherEntriesToDirectoryPathList(const WatcherEntries &watcherEntries) const + { + DirectoryPathIds directoryPathIds = Utils::transform( + watcherEntries, [&](WatcherEntry entry) { return entry.directoryPathId; }); + + std::sort(directoryPathIds.begin(), directoryPathIds.end()); + directoryPathIds.erase(std::unique(directoryPathIds.begin(), directoryPathIds.end()), + directoryPathIds.end()); + + return convertWatcherEntriesToDirectoryPathList(directoryPathIds); + } + + 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()); - } + 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 @@ -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()); + foundEntries.reserve(m_watchedEntries.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); - } + 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(entries, [](WatcherEntry entry) { + return entry.filePathId; + }); - std::set_intersection(m_watchedEntries.begin(), - m_watchedEntries.end(), - filePathIds.begin(), - filePathIds.end(), - std::back_inserter(watchedFilePathIds)); + std::sort(filePathIds.begin(), filePathIds.end()); - return watchedFilePathIds; + filePathIds.erase(std::unique(filePathIds.begin(), filePathIds.end()), filePathIds.end()); + + 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 m_changedFilePathCompressor; FileSystemWatcher m_fileSystemWatcher; + FileStatusCache m_fileStatusCache; + FileSystemInterface &m_fileSystem; FilePathCachingInterface &m_pathCache; ClangPathWatcherNotifier *m_notifier; + DirectoryPathCompressor m_directoryPathCompressor; }; } // namespace ClangBackEnd diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri index df537f58486..9b6bbdf3ee5 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 \ @@ -217,6 +223,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 00000000000..8969372919d --- /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 + +#include + +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; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/changedfilepathcompressor.h b/src/libs/clangsupport/directorypathcompressor.h similarity index 58% rename from src/libs/clangsupport/changedfilepathcompressor.h rename to src/libs/clangsupport/directorypathcompressor.h index aa8e1ec71aa..cd02948e834 100644 --- a/src/libs/clangsupport/changedfilepathcompressor.h +++ b/src/libs/clangsupport/directorypathcompressor.h @@ -27,53 +27,44 @@ #include "clangsupport_global.h" -#include -#include +#include "filepathcachinginterface.h" +#include #include -#include +#include #include namespace ClangBackEnd { -template -class ChangedFilePathCompressor +template +class DirectoryPathCompressor { public: - ChangedFilePathCompressor(FilePathCachingInterface &filePathCache) - : m_filePathCache(filePathCache) + DirectoryPathCompressor() { m_timer.setSingleShot(true); } + + virtual ~DirectoryPathCompressor() = default; + + void addDirectoryPathId(DirectoryPathId directoryPathIdId) { - m_timer.setSingleShot(true); - } + auto found = std::lower_bound(m_directoryPathIds.begin(), + m_directoryPathIds.end(), + directoryPathIdId); - virtual ~ChangedFilePathCompressor() - { - } - - void addFilePath(const QString &filePath) - { - FilePathId filePathId = m_filePathCache.filePathId(FilePath(filePath)); - - 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 &&callback) + virtual void setCallback(std::function &&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 00000000000..2fd0b5847e7 --- /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 + +#include + +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; + +} // namespace ClangBackEnd diff --git a/src/libs/clangsupport/filepathcache.h b/src/libs/clangsupport/filepathcache.h index bdd925a79c8..4845416d222 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 0a3fc6ee41b..372ed86bd3f 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 a3337755fd2..f69b940b157 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 b9668362f72..098fd39ca92 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 + template FilePathIds filePathIds(Container &&filePaths) const { FilePathIds filePathIds; diff --git a/src/libs/clangsupport/filepathexceptions.h b/src/libs/clangsupport/filepathexceptions.h index 80bded0c662..3174d894794 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 5917504dbf3..8aba2ef6d27 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(sourceId); + + if (!optionalDirectoryId) + throw SourceNameIdDoesNotExists(); + + transaction.commit(); + + return *optionalDirectoryId; + } catch (const Sqlite::StatementIsBusy &) { + return fetchDirectoryId(sourceId); + } + } + std::vector fetchAllSources() { try { diff --git a/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h b/src/libs/clangsupport/filepathstoragesqlitestatementfactory.h index dbf80717753..27296a1df65 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 similarity index 100% rename from src/tools/clangrefactoringbackend/source/filestatus.h rename to src/libs/clangsupport/filestatus.h diff --git a/src/libs/clangsupport/filestatuscache.cpp b/src/libs/clangsupport/filestatuscache.cpp new file mode 100644 index 00000000000..480b7455259 --- /dev/null +++ b/src/libs/clangsupport/filestatuscache.cpp @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 + +#include +#include + +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); +} + +namespace { +template +void set_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)) + callable(*first1++); + ++first2; + } + } +} + +template +void set_difference_call(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable) +{ + while (first1 != last1) { + if (first2 == last2) { + std::for_each(first1, last1, callable); + return; + } + if (*first1 < *first2) { + callable(*first1++); + } else { + if (!(*first2 < *first1)) + ++first1; + ++first2; + } + } +} +} // namespace + +void FileStatusCache::update(FilePathIds filePathIds) +{ + set_intersection_call(m_cacheEntries.begin(), + m_cacheEntries.end(), + filePathIds.begin(), + filePathIds.end(), + [&](auto &entry) { + entry.lastModified = m_fileSystem.lastModified(entry.filePathId); + }); +} + +FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const +{ + FilePathIds modifiedFilePathIds; + modifiedFilePathIds.reserve(filePathIds.size()); + + set_intersection_call(m_cacheEntries.begin(), + m_cacheEntries.end(), + filePathIds.begin(), + filePathIds.end(), + [&](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()); + + set_difference_call(filePathIds.begin(), + filePathIds.end(), + m_cacheEntries.begin(), + m_cacheEntries.end(), + [&](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 similarity index 69% rename from src/tools/clangrefactoringbackend/source/filestatuscache.h rename to src/libs/clangsupport/filestatuscache.h index 23195da6051..3f0af56b3ae 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; } -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 00000000000..5b8c037f80e --- /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 + +#include +#include +#include + +namespace ClangBackEnd { + +FilePathIds FileSystem::directoryEntries(const QString &directoryPath) const +{ + QDir directory{directoryPath}; + + QFileInfoList fileInfos = directory.entryInfoList(); + + FilePathIds filePathIds = Utils::transform(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 00000000000..ecf332de55c --- /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 00000000000..9d64d2a93a1 --- /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 + +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/utils/smallstringview.h b/src/libs/utils/smallstringview.h index 6052ff8777f..911ba2eafd4 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/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index 0ce49e829dd..d9e7dd3175d 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -179,7 +180,8 @@ struct Data // because we have a cycle dependency Sqlite::Database database; ClangBackEnd::RefactoringDatabaseInitializer databaseInitializer{database}; ClangBackEnd::FilePathCaching filePathCache{database}; - ClangPathWatcher includeWatcher{filePathCache}; + ClangBackEnd::FileSystem fileSystem{filePathCache}; + ClangPathWatcher includeWatcher{filePathCache, fileSystem}; ApplicationEnvironment environment; ProjectPartsStorage<> projectPartsStorage{database}; PrecompiledHeaderStorage<> preCompiledHeaderStorage{database}; diff --git a/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri b/src/tools/clangrefactoringbackend/source/clangrefactoringbackend-source.pri index e7e83da7884..a3da84d83b8 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 14ad48da143..00000000000 --- 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 -#include - -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/symbolindexing.h b/src/tools/clangrefactoringbackend/source/symbolindexing.h index 5c391503bff..bed279904dc 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexing.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexing.h @@ -39,6 +39,7 @@ #include #include +#include #include #include @@ -142,8 +143,9 @@ private: PrecompiledHeaderStorage m_precompiledHeaderStorage; ProjectPartsStorage m_projectPartsStorage; SymbolStorage m_symbolStorage; - ClangPathWatcher m_sourceWatcher{m_filePathCache}; - FileStatusCache m_fileStatusCache{m_filePathCache}; + FileSystem m_fileSytem{m_filePathCache}; + ClangPathWatcher m_sourceWatcher{m_filePathCache, m_fileSytem}; + FileStatusCache m_fileStatusCache{m_fileSytem}; SymbolsCollectorManager m_collectorManger; ProgressCounter m_progressCounter; std::function getModifiedTime{ diff --git a/tests/unit/unittest/changedfilepathcompressor-test.cpp b/tests/unit/unittest/changedfilepathcompressor-test.cpp deleted file mode 100644 index 08e66a2541d..00000000000 --- a/tests/unit/unittest/changedfilepathcompressor-test.cpp +++ /dev/null @@ -1,112 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2016 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of Qt Creator. -** -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -****************************************************************************/ - -#include "googletest.h" - -#include "mocktimer.h" - -#include -#include -#include - -namespace { - -using testing::ElementsAre; -using testing::Invoke; -using testing::IsEmpty; -using testing::NiceMock; - -using ClangBackEnd::FilePath; -using ClangBackEnd::FilePathId; - -class ChangedFilePathCompressor : public testing::Test -{ -protected: - void SetUp() - { - compressor.setCallback(mockCompressorCallback.AsStdFunction()); - } - - FilePathId filePathId(const QString &filePath) - { - Utils::SmallString utf8FilePath{filePath}; - - return filePathCache.filePathId(ClangBackEnd::FilePathView{utf8FilePath}); - } - -protected: - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - ClangBackEnd::RefactoringDatabaseInitializer initializer{database}; - ClangBackEnd::FilePathCaching filePathCache{database}; - NiceMock> mockCompressorCallback; - ClangBackEnd::ChangedFilePathCompressor> compressor{filePathCache}; - NiceMock &mockTimer = compressor.timer(); - QString filePath1{"filePath1"}; - QString filePath2{"filePath2"}; - FilePathId filePathId1 = filePathId(filePath1); - FilePathId filePathId2 = filePathId(filePath2); -}; - -TEST_F(ChangedFilePathCompressor, AddFilePath) -{ - compressor.addFilePath(filePath1); - - ASSERT_THAT(compressor.takeFilePathIds(), ElementsAre(filePathId(filePath1))); -} - -TEST_F(ChangedFilePathCompressor, NoFilePathsAferTakenThem) -{ - compressor.addFilePath(filePath1); - - compressor.takeFilePathIds(); - - ASSERT_THAT(compressor.takeFilePathIds(), IsEmpty()); -} - -TEST_F(ChangedFilePathCompressor, CallRestartTimerAfterAddingPath) -{ - EXPECT_CALL(mockTimer, start(20)); - - compressor.addFilePath(filePath1); -} - -TEST_F(ChangedFilePathCompressor, CallTimeOutAfterAddingPath) -{ - EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(filePathId1, filePathId2))); - - compressor.addFilePath(filePath1); - compressor.addFilePath(filePath2); -} - -TEST_F(ChangedFilePathCompressor, RemoveDuplicates) -{ - EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(filePathId1, filePathId2))); - - compressor.addFilePath(filePath1); - compressor.addFilePath(filePath2); - compressor.addFilePath(filePath1); -} - -} diff --git a/tests/unit/unittest/clangpathwatcher-test.cpp b/tests/unit/unittest/clangpathwatcher-test.cpp index 00fe91dc496..f4d99e358d7 100644 --- a/tests/unit/unittest/clangpathwatcher-test.cpp +++ b/tests/unit/unittest/clangpathwatcher-test.cpp @@ -25,10 +25,11 @@ #include "googletest.h" -#include "mocktimer.h" -#include "mockfilepathcaching.h" -#include "mockqfilesystemwatcher.h" #include "mockclangpathwatchernotifier.h" +#include "mockfilepathcaching.h" +#include "mockfilesystem.h" +#include "mockqfilesystemwatcher.h" +#include "mocktimer.h" #include @@ -55,91 +56,126 @@ using ClangBackEnd::WatcherEntry; class ClangPathWatcher : public testing::Test { protected: - void SetUp(); + void SetUp() + { + ON_CALL(mockFilePathCache, filePathId(Eq(path1))).WillByDefault(Return(pathIds[0])); + ON_CALL(mockFilePathCache, filePathId(Eq(path2))).WillByDefault(Return(pathIds[1])); + ON_CALL(mockFilePathCache, filePathId(Eq(path3))).WillByDefault(Return(pathIds[2])); + ON_CALL(mockFilePathCache, filePathId(Eq(path4))).WillByDefault(Return(pathIds[3])); + ON_CALL(mockFilePathCache, filePathId(Eq(path5))).WillByDefault(Return(pathIds[4])); + ON_CALL(mockFilePathCache, filePath(Eq(pathIds[0]))).WillByDefault(Return(FilePath{path1})); + ON_CALL(mockFilePathCache, filePath(Eq(pathIds[1]))).WillByDefault(Return(FilePath{path2})); + ON_CALL(mockFilePathCache, filePath(Eq(pathIds[2]))).WillByDefault(Return(FilePath{path3})); + ON_CALL(mockFilePathCache, filePath(Eq(pathIds[3]))).WillByDefault(Return(FilePath{path4})); + ON_CALL(mockFilePathCache, filePath(Eq(pathIds[4]))).WillByDefault(Return(FilePath{path5})); + ON_CALL(mockFilePathCache, directoryPathId(TypedEq(pathIds[0]))) + .WillByDefault(Return(directoryPaths[0])); + ON_CALL(mockFilePathCache, directoryPathId(TypedEq(pathIds[1]))) + .WillByDefault(Return(directoryPaths[0])); + ON_CALL(mockFilePathCache, directoryPathId(TypedEq(pathIds[2]))) + .WillByDefault(Return(directoryPaths[1])); + ON_CALL(mockFilePathCache, directoryPathId(TypedEq(pathIds[3]))) + .WillByDefault(Return(directoryPaths[1])); + ON_CALL(mockFilePathCache, directoryPathId(TypedEq(pathIds[4]))) + .WillByDefault(Return(directoryPaths[2])); + ON_CALL(mockFileSystem, lastModified(_)).WillByDefault(Return(1)); + ON_CALL(mockFilePathCache, + directoryPathId(TypedEq(directoryPathString))) + .WillByDefault(Return(directoryPaths[0])); + ON_CALL(mockFilePathCache, + directoryPathId(TypedEq(directoryPathString2))) + .WillByDefault(Return(directoryPaths[1])); + ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[0]))) + .WillByDefault(Return(directoryPath)); + ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[1]))) + .WillByDefault(Return(directoryPath2)); + ON_CALL(mockFilePathCache, directoryPath(Eq(directoryPaths[2]))) + .WillByDefault(Return(directoryPath3)); + ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath))) + .WillByDefault(Return(FilePathIds{pathIds[0], pathIds[1]})); + ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath2))) + .WillByDefault(Return(FilePathIds{pathIds[2], pathIds[3]})); + ON_CALL(mockFileSystem, directoryEntries(Eq(directoryPath3))) + .WillByDefault(Return(FilePathIds{pathIds[4]})); + } static WatcherEntries sorted(WatcherEntries &&entries) { std::stable_sort(entries.begin(), entries.end()); - return entries; + return std::move(entries); } protected: - NiceMock filePathCache; + NiceMock mockFilePathCache; NiceMock notifier; - Watcher watcher{filePathCache, ¬ifier}; + NiceMock mockFileSystem; + Watcher watcher{mockFilePathCache, mockFileSystem, ¬ifier}; NiceMock &mockQFileSytemWatcher = watcher.fileSystemWatcher(); ProjectPartId id1{2}; ProjectPartId id2{3}; ProjectPartId id3{4}; FilePathView path1{"/path/path1"}; FilePathView path2{"/path/path2"}; + FilePathView path3{"/path2/path1"}; + FilePathView path4{"/path2/path2"}; + FilePathView path5{"/path3/path"}; QString path1QString = QString(path1.toStringView()); QString path2QString = QString(path2.toStringView()); - FilePathIds pathIds = {1, 2}; + QString directoryPath = "/path"; + QString directoryPath2 = "/path2"; + QString directoryPath3 = "/path3"; + Utils::PathString directoryPathString = directoryPath; + Utils::PathString directoryPathString2 = directoryPath2; + FilePathIds pathIds = {1, 2, 3, 4, 5}; + ClangBackEnd::DirectoryPathIds directoryPaths = {1, 2, 3}; ClangBackEnd::ProjectPartIds ids{id1, id2, id3}; - WatcherEntry watcherEntry1{ids[0], pathIds[0]}; - WatcherEntry watcherEntry2{ids[1], pathIds[0]}; - WatcherEntry watcherEntry3{ids[0], pathIds[1]}; - WatcherEntry watcherEntry4{ids[1], pathIds[1]}; - WatcherEntry watcherEntry5{ids[2], pathIds[1]}; + WatcherEntry watcherEntry1{ids[0], directoryPaths[0], pathIds[0]}; + WatcherEntry watcherEntry2{ids[1], directoryPaths[0], pathIds[0]}; + WatcherEntry watcherEntry3{ids[0], directoryPaths[0], pathIds[1]}; + WatcherEntry watcherEntry4{ids[1], directoryPaths[0], pathIds[1]}; + WatcherEntry watcherEntry5{ids[2], directoryPaths[0], pathIds[1]}; + WatcherEntry watcherEntry6{ids[0], directoryPaths[1], pathIds[2]}; + WatcherEntry watcherEntry7{ids[1], directoryPaths[1], pathIds[3]}; }; -TEST_F(ClangPathWatcher, ConvertWatcherEntriesToQStringList) -{ - auto convertedList = watcher.convertWatcherEntriesToQStringList(sorted({watcherEntry1, watcherEntry3})); - - ASSERT_THAT(convertedList, ElementsAre(path1QString, path2QString)); -} - -TEST_F(ClangPathWatcher, UniquePaths) -{ - auto uniqueEntries = watcher.uniquePaths(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4})); - - ASSERT_THAT(uniqueEntries, ElementsAre(watcherEntry1, watcherEntry3)); -} - -TEST_F(ClangPathWatcher, NotWatchedEntries) -{ - watcher.addEntries({watcherEntry1, watcherEntry4}); - - auto newEntries = watcher.notWatchedEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4})); - - ASSERT_THAT(newEntries, ElementsAre(watcherEntry2, watcherEntry3)); -} - TEST_F(ClangPathWatcher, AddIdPaths) { - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString, path2QString})); + EXPECT_CALL(mockQFileSytemWatcher, + addPaths(UnorderedElementsAre(QString(directoryPath), QString(directoryPath2)))); - watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}}); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); } TEST_F(ClangPathWatcher, UpdateIdPathsCallsAddPathInFileWatcher) { - watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[0]}}}); - - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path2QString})); - watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}}); + + EXPECT_CALL(mockQFileSytemWatcher, addPaths(UnorderedElementsAre(QString(directoryPath2)))); + + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); } TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPathsCallsRemovePathInFileWatcher) { - watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[0]}}}); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); - EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString})); + EXPECT_CALL(mockQFileSytemWatcher, removePaths(UnorderedElementsAre(QString(directoryPath2)))); - watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[0]}}}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}}); } TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPathsDoNotCallsRemovePathInFileWatcher) { - watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[0]}}}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}}, + {id2, {pathIds[0], pathIds[1], pathIds[3]}}, + {id3, {pathIds[0]}}}); - EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString})) - .Times(0); + EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0); - watcher.updateIdPaths({{id1, {pathIds[1]}}, {id2, {pathIds[0]}}}); + watcher.updateIdPaths({{id1, {pathIds[1]}}, {id2, {pathIds[3]}}}); } TEST_F(ClangPathWatcher, UpdateIdPathsAndRemoveUnusedPaths) @@ -165,36 +201,18 @@ TEST_F(ClangPathWatcher, ExtractSortedIdsFromConvertIdPaths) ASSERT_THAT(entriesAndIds.second, ElementsAre(ids[0], ids[1], ids[2])); } -TEST_F(ClangPathWatcher, NotWatchedPaths) -{ - watcher.mergeToWatchedEntries(sorted({watcherEntry1})); - - auto newEntries = watcher.notWatchedPaths({watcherEntry2, watcherEntry3}); - - ASSERT_THAT(newEntries, ElementsAre(watcherEntry3)); -} - -TEST_F(ClangPathWatcher, AddedPaths) -{ - watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry2})); - - auto filteredEntries = watcher.filterNotWatchedPaths({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4}); - - ASSERT_THAT(filteredEntries, ElementsAre(watcherEntry3)); -} - TEST_F(ClangPathWatcher, MergeEntries) { - watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry4})); + watcher.updateIdPaths({{id1, {pathIds[0]}}, {id2, {pathIds[1]}}}); ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4)); } TEST_F(ClangPathWatcher, MergeMoreEntries) { - watcher.mergeToWatchedEntries(sorted({watcherEntry1, watcherEntry4})); + watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1]}}}); - watcher.mergeToWatchedEntries(sorted({watcherEntry2, watcherEntry3})); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}}); ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4)); } @@ -204,48 +222,48 @@ TEST_F(ClangPathWatcher, AddEmptyEntries) EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)) .Times(0); - watcher.addEntries({}); + watcher.updateIdPaths({}); } TEST_F(ClangPathWatcher, AddEntriesWithSameIdAndDifferentPaths) { - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString, path2QString})); + EXPECT_CALL(mockQFileSytemWatcher, + addPaths(ElementsAre(directoryPath, directoryPath2, directoryPath3))); - watcher.addEntries({watcherEntry1, watcherEntry3}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[4]}}}); } TEST_F(ClangPathWatcher, AddEntriesWithDifferentIdAndSamePaths) { - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString})); + EXPECT_CALL(mockQFileSytemWatcher, addPaths(ElementsAre(directoryPath))); - watcher.addEntries({watcherEntry1, watcherEntry2}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1]}}}); } TEST_F(ClangPathWatcher, DontAddNewEntriesWithSameIdAndSamePaths) { - watcher.addEntries({watcherEntry1}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}}); - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString})) - .Times(0); + EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0); - watcher.addEntries({watcherEntry1}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}}); } TEST_F(ClangPathWatcher, DontAddNewEntriesWithDifferentIdAndSamePaths) { - watcher.addEntries({watcherEntry1}); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}}); - EXPECT_CALL(mockQFileSytemWatcher, addPaths(QStringList{path1QString})) - .Times(0); + EXPECT_CALL(mockQFileSytemWatcher, addPaths(_)).Times(0); - watcher.addEntries({watcherEntry2}); + watcher.updateIdPaths({{id2, {pathIds[0], pathIds[1], pathIds[2], pathIds[3], pathIds[4]}}}); } TEST_F(ClangPathWatcher, RemoveEntriesWithId) { - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5})); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[1]}}}); - watcher.removeIdsFromWatchedEntries({ids[0]}); + watcher.removeIds({id1}); ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry2, watcherEntry4, watcherEntry5)); } @@ -260,37 +278,52 @@ TEST_F(ClangPathWatcher, RemoveNoPathsForEmptyIds) TEST_F(ClangPathWatcher, RemoveNoPathsForOneId) { - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4})); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)) .Times(0); - watcher.removeIds({id1}); + watcher.removeIds({id3}); } TEST_F(ClangPathWatcher, RemovePathForOneId) { - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3})); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); - EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path2QString})); + EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath2))); - watcher.removeIds({id1}); + watcher.removeIds({id2}); +} + +TEST_F(ClangPathWatcher, RemoveNoPathSecondTime) +{ + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); + watcher.removeIds({id2}); + + EXPECT_CALL(mockQFileSytemWatcher, removePaths(_)).Times(0); + + watcher.removeIds({id2}); } TEST_F(ClangPathWatcher, RemoveAllPathsForThreeId) { - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5})); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); - EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path1QString, path2QString})); + EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath, directoryPath2))); watcher.removeIds({id1, id2, id3}); } TEST_F(ClangPathWatcher, RemoveOnePathForTwoId) { - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5})); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1]}}, {id2, {pathIds[0], pathIds[1]}}, {id3, {pathIds[3]}}}); - EXPECT_CALL(mockQFileSytemWatcher, removePaths(QStringList{path1QString})); + EXPECT_CALL(mockQFileSytemWatcher, removePaths(ElementsAre(directoryPath))); watcher.removeIds({id1, id2}); } @@ -313,71 +346,51 @@ TEST_F(ClangPathWatcher, RemoveUnusedEntries) ASSERT_THAT(watcher.watchedEntries(), ElementsAre(watcherEntry1, watcherEntry4, watcherEntry5)); } -TEST_F(ClangPathWatcher, EmptyVectorNotifyFileChange) +TEST_F(ClangPathWatcher, TwoNotifyFileChanges) { - watcher.addEntries({watcherEntry3}); - - EXPECT_CALL(notifier, pathsWithIdsChanged(IsEmpty())); - - mockQFileSytemWatcher.fileChanged(path1QString); -} - -TEST_F(ClangPathWatcher, NotifyFileChange) -{ - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5})); + watcher.updateIdPaths({{id1, {pathIds[0], pathIds[1], pathIds[2]}}, + {id2, {pathIds[0], pathIds[1], pathIds[3]}}, + {id3, {pathIds[4]}}}); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2)); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[1]))).WillByDefault(Return(2)); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[3]))).WillByDefault(Return(2)); EXPECT_CALL(notifier, pathsWithIdsChanged(ElementsAre(id1, id2))); - mockQFileSytemWatcher.fileChanged(path1QString); -} - -TEST_F(ClangPathWatcher, TwoNotifyFileChanges) -{ - watcher.addEntries(sorted({watcherEntry1, watcherEntry2, watcherEntry3, watcherEntry4, watcherEntry5})); - - EXPECT_CALL(notifier, pathsWithIdsChanged(ElementsAre(id1, id2, id3))); - - mockQFileSytemWatcher.fileChanged(path2QString); - mockQFileSytemWatcher.fileChanged(path1QString); + mockQFileSytemWatcher.directoryChanged(directoryPath); + mockQFileSytemWatcher.directoryChanged(directoryPath2); } TEST_F(ClangPathWatcher, NotifyForPathChanges) { - watcher.addEntries({watcherEntry1}); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2)); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[3]))).WillByDefault(Return(2)); EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0]))); - mockQFileSytemWatcher.fileChanged(path1QString); + mockQFileSytemWatcher.directoryChanged(directoryPath); } TEST_F(ClangPathWatcher, NoNotifyForUnwatchedPathChanges) { - watcher.addEntries({watcherEntry3}); + watcher.updateIdPaths({{id1, {pathIds[3]}}, {id2, {pathIds[3]}}}); EXPECT_CALL(notifier, pathsChanged(IsEmpty())); - mockQFileSytemWatcher.fileChanged(path1QString); + mockQFileSytemWatcher.directoryChanged(directoryPath); } TEST_F(ClangPathWatcher, NoDuplicatePathChanges) { - watcher.addEntries({watcherEntry1}); + watcher.updateIdPaths( + {{id1, {pathIds[0], pathIds[1], pathIds[2]}}, {id2, {pathIds[0], pathIds[1], pathIds[3]}}}); + ON_CALL(mockFileSystem, lastModified(Eq(pathIds[0]))).WillByDefault(Return(2)); EXPECT_CALL(notifier, pathsChanged(ElementsAre(pathIds[0]))); - mockQFileSytemWatcher.fileChanged(path1QString); - mockQFileSytemWatcher.fileChanged(path1QString); -} - -void ClangPathWatcher::SetUp() -{ - ON_CALL(filePathCache, filePathId(Eq(path1))) - .WillByDefault(Return(pathIds[0])); - ON_CALL(filePathCache, filePathId(Eq(path2))) - .WillByDefault(Return(pathIds[1])); - ON_CALL(filePathCache, filePath(pathIds[0])) - .WillByDefault(Return(FilePath{path1})); - ON_CALL(filePathCache, filePath(Eq(pathIds[1]))) - .WillByDefault(Return(FilePath{path2})); -} + mockQFileSytemWatcher.directoryChanged(directoryPath); + mockQFileSytemWatcher.directoryChanged(directoryPath); } +} // namespace diff --git a/tests/unit/unittest/directorypathcompressor-test.cpp b/tests/unit/unittest/directorypathcompressor-test.cpp new file mode 100644 index 00000000000..50853e8e1c5 --- /dev/null +++ b/tests/unit/unittest/directorypathcompressor-test.cpp @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2016 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "googletest.h" + +#include "mockfilesystem.h" +#include "mocktimer.h" + +#include + +namespace { + +using testing::ElementsAre; +using testing::Invoke; +using testing::IsEmpty; +using testing::NiceMock; + +using ClangBackEnd::DirectoryPathId; +using ClangBackEnd::DirectoryPathIds; + +class DirectoryPathCompressor : public testing::Test +{ +protected: + void SetUp() + { + compressor.setCallback(mockCompressorCallback.AsStdFunction()); + } + +protected: + NiceMock> mockCompressorCallback; + ClangBackEnd::DirectoryPathCompressor> compressor; + NiceMock &mockTimer = compressor.timer(); + DirectoryPathId directoryPathId1{1}; + DirectoryPathId directoryPathId2{2}; +}; + +TEST_F(DirectoryPathCompressor, AddFilePath) +{ + compressor.addDirectoryPathId(directoryPathId1); + + ASSERT_THAT(compressor.takeDirectoryPathIds(), ElementsAre(directoryPathId1)); +} + +TEST_F(DirectoryPathCompressor, NoFilePathsAferTakenThem) +{ + compressor.addDirectoryPathId(directoryPathId1); + + compressor.takeDirectoryPathIds(); + + ASSERT_THAT(compressor.takeDirectoryPathIds(), IsEmpty()); +} + +TEST_F(DirectoryPathCompressor, CallRestartTimerAfterAddingPath) +{ + EXPECT_CALL(mockTimer, start(20)); + + compressor.addDirectoryPathId(directoryPathId1); +} + +TEST_F(DirectoryPathCompressor, CallTimeOutAfterAddingPath) +{ + EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(directoryPathId1, directoryPathId2))); + + compressor.addDirectoryPathId(directoryPathId1); + compressor.addDirectoryPathId(directoryPathId2); +} + +TEST_F(DirectoryPathCompressor, RemoveDuplicates) +{ + EXPECT_CALL(mockCompressorCallback, Call(ElementsAre(directoryPathId1, directoryPathId2))); + + compressor.addDirectoryPathId(directoryPathId1); + compressor.addDirectoryPathId(directoryPathId2); + compressor.addDirectoryPathId(directoryPathId1); +} + +} diff --git a/tests/unit/unittest/filepathcache-test.cpp b/tests/unit/unittest/filepathcache-test.cpp index 43b5b026bbf..478e5f02153 100644 --- a/tests/unit/unittest/filepathcache-test.cpp +++ b/tests/unit/unittest/filepathcache-test.cpp @@ -31,6 +31,7 @@ namespace { +using ClangBackEnd::DirectoryPathId; using ClangBackEnd::FilePathId; using Cache = ClangBackEnd::FilePathCache>; using ClangBackEnd::FilePathId; @@ -166,4 +167,153 @@ TEST_F(FilePathCache, DuplicateFilePathsAreEqual) ASSERT_THAT(filePath2Id, Eq(filePath1Id)); } +TEST_F(FilePathCache, DirectoryPathIdCallsFetchDirectoryId) +{ + EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to"))); + + cache.directoryPathId(Utils::SmallString("/path/to")); } + +TEST_F(FilePathCache, SecondDirectoryPathIdCallsNotFetchDirectoryId) +{ + cache.directoryPathId(Utils::SmallString("/path/to")); + + EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to"))).Times(0); + + cache.directoryPathId(Utils::SmallString("/path/to")); +} + +TEST_F(FilePathCache, DirectoryPathIdWithTrailingSlash) +{ + EXPECT_CALL(mockStorage, fetchDirectoryId(Eq("/path/to"))); + + cache.directoryPathId(Utils::SmallString("/path/to/")); +} + +TEST_F(FilePathCache, DirectoryPathId) +{ + auto id = cache.directoryPathId(Utils::SmallString("/path/to")); + + ASSERT_THAT(id, Eq(DirectoryPathId{5})); +} + +TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCache) +{ + auto firstId = cache.directoryPathId(Utils::SmallString("/path/to")); + + auto secondId = cache.directoryPathId(Utils::SmallString("/path/to")); + + ASSERT_THAT(secondId, firstId); +} + +TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithTrailingSlash) +{ + auto firstId = cache.directoryPathId(Utils::SmallString("/path/to/")); + + auto secondId = cache.directoryPathId(Utils::SmallString("/path/to/")); + + ASSERT_THAT(secondId, firstId); +} + +TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithAndWithoutTrailingSlash) +{ + auto firstId = cache.directoryPathId(Utils::SmallString("/path/to/")); + + auto secondId = cache.directoryPathId(Utils::SmallString("/path/to")); + + ASSERT_THAT(secondId, firstId); +} + +TEST_F(FilePathCache, DirectoryPathIdIsAlreadyInCacheWithoutAndWithTrailingSlash) +{ + auto firstId = cache.directoryPathId(Utils::SmallString("/path/to")); + + auto secondId = cache.directoryPathId(Utils::SmallString("/path/to/")); + + ASSERT_THAT(secondId, firstId); +} + +TEST_F(FilePathCache, ThrowForGettingADirectoryPathWithAnInvalidId) +{ + DirectoryPathId directoryPathId; + + ASSERT_THROW(cache.directoryPath(directoryPathId), + ClangBackEnd::NoDirectoryPathForInvalidDirectoryPathId); +} + +TEST_F(FilePathCache, GetADirectoryPath) +{ + DirectoryPathId directoryPathId{5}; + + auto directoryPath = cache.directoryPath(directoryPathId); + + ASSERT_THAT(directoryPath, Eq(Utils::SmallStringView{"/path/to"})); +} + +TEST_F(FilePathCache, GetADirectoryPathWithCachedDirectoryPathId) +{ + DirectoryPathId directoryPathId{5}; + cache.directoryPath(directoryPathId); + + auto directoryPath = cache.directoryPath(directoryPathId); + + ASSERT_THAT(directoryPath, Eq(Utils::SmallStringView{"/path/to"})); +} + +TEST_F(FilePathCache, DirectoryPathCallsFetchDirectoryPath) +{ + EXPECT_CALL(mockStorage, fetchDirectoryPath(Eq(DirectoryPathId{5}))); + + cache.directoryPath(5); +} + +TEST_F(FilePathCache, SecondDirectoryPathCallsNotFetchDirectoryPath) +{ + cache.directoryPath(5); + + EXPECT_CALL(mockStorage, fetchDirectoryPath(_)).Times(0); + + cache.directoryPath(5); +} + +TEST_F(FilePathCache, ThrowForGettingADirectoryPathIdWithAnInvalidFilePathId) +{ + FilePathId filePathId; + + ASSERT_THROW(cache.directoryPathId(filePathId), ClangBackEnd::NoFilePathForInvalidFilePathId); +} + +TEST_F(FilePathCache, FetchDirectoryPathIdByFilePathId) +{ + auto directoryId = cache.directoryPathId(42); + + ASSERT_THAT(directoryId, Eq(5)); +} + +TEST_F(FilePathCache, FetchDirectoryPathIdByFilePathIdCached) +{ + cache.directoryPathId(42); + + auto directoryId = cache.directoryPathId(42); + + ASSERT_THAT(directoryId, Eq(5)); +} + +TEST_F(FilePathCache, FetchFilePathAfterFetchingDirectoryIdByFilePathId) +{ + cache.directoryPathId(42); + + auto filePath = cache.filePath(42); + + ASSERT_THAT(filePath, Eq("/path/to/file.cpp")); +} + +TEST_F(FilePathCache, FetchDirectoryPathIdAfterFetchingFilePathByFilePathId) +{ + cache.filePath(42); + + auto directoryId = cache.directoryPathId(42); + + ASSERT_THAT(directoryId, Eq(5)); +} +} // namespace diff --git a/tests/unit/unittest/filepathstorage-test.cpp b/tests/unit/unittest/filepathstorage-test.cpp index 37f325daf05..fb60e32512d 100644 --- a/tests/unit/unittest/filepathstorage-test.cpp +++ b/tests/unit/unittest/filepathstorage-test.cpp @@ -46,8 +46,8 @@ protected: void SetUp() { ON_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(_)) - .WillByDefault(Return(Utils::optional())); + valueReturnInt32(A())) + .WillByDefault(Return(Utils::optional())); ON_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, valueReturnInt32(Utils::SmallStringView(""))) .WillByDefault(Return(Utils::optional(0))); @@ -77,9 +77,12 @@ protected: ON_CALL(selectSourceNameAndDirectoryIdFromSourcesBySourceId, valueReturnSourceNameAndDirectoryId(42)) .WillByDefault(Return(Utils::optional({"file.cpp", 5}))); + ON_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq(42))) + .WillByDefault(Return(Utils::optional(5))); - EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, valueReturnInt32(_)) - .Times(AnyNumber()); + EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, + valueReturnInt32(A())) + .Times(AnyNumber()); EXPECT_CALL(selectSourceIdFromSourcesByDirectoryIdAndSourceName, valueReturnInt32(_, _)) .Times(AnyNumber()); EXPECT_CALL(insertIntoDirectories, write(An())) @@ -107,6 +110,7 @@ protected: MockSqliteWriteStatement &insertIntoDirectories = factory.insertIntoDirectories; MockSqliteWriteStatement &insertIntoSources = factory.insertIntoSources; MockSqliteReadStatement &selectAllSources = factory.selectAllSources; + MockSqliteReadStatement &selectDirectoryIdFromSourcesBySourceId = factory.selectDirectoryIdFromSourcesBySourceId; Storage storage{factory}; }; @@ -229,7 +233,7 @@ TEST_F(FilePathStorage, CallSelectForFetchingDirectoryIdForKnownPath) EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/path/to"))); + valueReturnInt32(TypedEq("/path/to"))); EXPECT_CALL(mockDatabase, commit()); storage.fetchDirectoryId("/path/to"); @@ -267,7 +271,7 @@ TEST_F(FilePathStorage, CallSelectAndWriteForFetchingDirectoryIdForUnknownPath) EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/some/not/known/path"))); + valueReturnInt32(TypedEq("/some/not/known/path"))); EXPECT_CALL(insertIntoDirectories, write(TypedEq("/some/not/known/path"))); EXPECT_CALL(mockDatabase, commit()); @@ -296,7 +300,7 @@ TEST_F(FilePathStorage, RestartFetchDirectoryIDIfTheStatementIsBusyInBeginBecaus EXPECT_CALL(mockDatabase, rollback()).Times(0); EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/other/unknow/path"))); + valueReturnInt32(TypedEq("/other/unknow/path"))); EXPECT_CALL(insertIntoDirectories, write(TypedEq("/other/unknow/path"))); EXPECT_CALL(mockDatabase, commit()); @@ -310,13 +314,13 @@ TEST_F(FilePathStorage, EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/other/unknow/path"))); + valueReturnInt32(TypedEq("/other/unknow/path"))); EXPECT_CALL(insertIntoDirectories, write(TypedEq("/other/unknow/path"))) .WillOnce(Throw(Sqlite::StatementIsBusy("busy"))); EXPECT_CALL(mockDatabase, rollback()); EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/other/unknow/path"))); + valueReturnInt32(TypedEq("/other/unknow/path"))); EXPECT_CALL(insertIntoDirectories, write(TypedEq("/other/unknow/path"))); EXPECT_CALL(mockDatabase, commit()); @@ -329,13 +333,13 @@ TEST_F(FilePathStorage, CallSelectAndWriteForFetchingDirectoryIdTwoTimesIfTheInd EXPECT_CALL(mockDatabase,deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/other/unknow/path"))); + valueReturnInt32(TypedEq("/other/unknow/path"))); EXPECT_CALL(insertIntoDirectories, write(TypedEq("/other/unknow/path"))) .WillOnce(Throw(Sqlite::ConstraintPreventsModification("busy"))); EXPECT_CALL(mockDatabase, rollback()); EXPECT_CALL(mockDatabase,deferredBegin()); EXPECT_CALL(selectDirectoryIdFromDirectoriesByDirectoryPath, - valueReturnInt32(Eq("/other/unknow/path"))); + valueReturnInt32(TypedEq("/other/unknow/path"))); EXPECT_CALL(mockDatabase, commit()); storage.fetchDirectoryId("/other/unknow/path"); @@ -368,7 +372,6 @@ TEST_F(FilePathStorage, EXPECT_CALL(insertIntoSources, write(TypedEq(5), TypedEq("otherunknownfile.h"))) .WillOnce(Throw(Sqlite::StatementIsBusy("busy"))); - ; EXPECT_CALL(mockDatabase, rollback()); EXPECT_CALL(mockDatabase, deferredBegin()); EXPECT_CALL(selectSourceIdFromSourcesByDirectoryIdAndSourceName, @@ -545,4 +548,59 @@ TEST_F(FilePathStorage, RestartFetchAllSourcesIfBeginIsBusy) storage.fetchAllSources(); } +TEST_F(FilePathStorage, FetchDirectoryIdForUnknownFileID) +{ + ASSERT_THROW(storage.fetchDirectoryId(1111), ClangBackEnd::SourceNameIdDoesNotExists); } + +TEST_F(FilePathStorage, FetchDirectoryId) +{ + auto directoryId = storage.fetchDirectoryId(42); + + ASSERT_THAT(directoryId, 5); +} + +TEST_F(FilePathStorage, FetchDirectoryIdCalls) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, lock()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq(42))); + EXPECT_CALL(mockDatabase, commit()); + EXPECT_CALL(mockDatabase, unlock()); + + storage.fetchDirectoryId(42); +} + +TEST_F(FilePathStorage, FetchDirectoryIdCallsDatabaseIsBusy) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, lock()); + EXPECT_CALL(mockDatabase, deferredBegin()).WillOnce(Throw(Sqlite::StatementIsBusy("busy"))); + EXPECT_CALL(mockDatabase, rollback()).Times(0); + EXPECT_CALL(mockDatabase, unlock()); + EXPECT_CALL(mockDatabase, lock()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq(42))); + EXPECT_CALL(mockDatabase, commit()); + EXPECT_CALL(mockDatabase, unlock()); + + storage.fetchDirectoryId(42); +} + +TEST_F(FilePathStorage, FetchDirectoryIdCallsThrows) +{ + InSequence s; + + EXPECT_CALL(mockDatabase, lock()); + EXPECT_CALL(mockDatabase, deferredBegin()); + EXPECT_CALL(selectDirectoryIdFromSourcesBySourceId, valueReturnInt32(TypedEq(41))); + EXPECT_CALL(mockDatabase, rollback()); + EXPECT_CALL(mockDatabase, unlock()); + + ASSERT_ANY_THROW(storage.fetchDirectoryId(41)); +} + +} // namespace diff --git a/tests/unit/unittest/filepathstoragesqlitestatementfactory-test.cpp b/tests/unit/unittest/filepathstoragesqlitestatementfactory-test.cpp index ac902fa7001..40af2d99c3e 100644 --- a/tests/unit/unittest/filepathstoragesqlitestatementfactory-test.cpp +++ b/tests/unit/unittest/filepathstoragesqlitestatementfactory-test.cpp @@ -61,7 +61,13 @@ TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceIdFromSourcesByDirecto Eq("SELECT sourceId FROM sources WHERE directoryId = ? AND sourceName = ?")); } -TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameFromSourcesByDirectoryIdAndSourceId) +TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameAndDirectoryIdFromSourcesByAndSourceId) +{ + ASSERT_THAT(factory.selectSourceNameAndDirectoryIdFromSourcesBySourceId.sqlStatement, + Eq("SELECT sourceName, directoryId FROM sources WHERE sourceId = ?")); +} + +TEST_F(FilePathStorageSqliteStatementFactory, SelectSourceNameAndDirectoryIdBySourceId) { ASSERT_THAT(factory.selectSourceNameAndDirectoryIdFromSourcesBySourceId.sqlStatement, Eq("SELECT sourceName, directoryId FROM sources WHERE sourceId = ?")); diff --git a/tests/unit/unittest/filestatuscache-test.cpp b/tests/unit/unittest/filestatuscache-test.cpp index db13bc28a7c..07ba02655bc 100644 --- a/tests/unit/unittest/filestatuscache-test.cpp +++ b/tests/unit/unittest/filestatuscache-test.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "googletest.h" +#include "mockfilesystem.h" #include #include @@ -37,31 +38,33 @@ namespace { using ClangBackEnd::FilePathId; +using ClangBackEnd::FilePathIds; class FileStatusCache : public testing::Test { protected: - FilePathId filePathId(Utils::SmallStringView path) const + FileStatusCache() { - return filePathCache.filePathId(ClangBackEnd::FilePathView(path)); - } - - void touchFile(FilePathId filePathId) - { - std::ofstream ostream(std::string(filePathCache.filePath(filePathId)), std::ios::binary); - ostream.write("\n", 1); - ostream.close(); + ON_CALL(fileSystem, lastModified(Eq(header))).WillByDefault(Return(headerLastModifiedTime)); + ON_CALL(fileSystem, lastModified(Eq(source))).WillByDefault(Return(sourceLastModifiedTime)); + ON_CALL(fileSystem, lastModified(Eq(header2))).WillByDefault(Return(header2LastModifiedTime)); + ON_CALL(fileSystem, lastModified(Eq(source2))).WillByDefault(Return(source2LastModifiedTime)); } protected: - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - ClangBackEnd::RefactoringDatabaseInitializer databaseInitializer{database}; - ClangBackEnd::FilePathCaching filePathCache{database}; - ClangBackEnd::FileStatusCache cache{filePathCache}; - FilePathId header{filePathId(TESTDATA_DIR "/filestatuscache_header.h")}; - FilePathId source{filePathId(TESTDATA_DIR "/filestatuscache_header.cpp")}; - long long headerLastModifiedTime = QFileInfo(TESTDATA_DIR "/filestatuscache_header.h").lastModified().toSecsSinceEpoch(); - long long sourceLastModifiedTime = QFileInfo(TESTDATA_DIR "/filestatuscache_header.cpp").lastModified().toSecsSinceEpoch(); + NiceMock fileSystem; + ClangBackEnd::FileStatusCache cache{fileSystem}; + FilePathId header{1}; + FilePathId source{2}; + FilePathId header2{3}; + FilePathId source2{4}; + FilePathIds entries{header, source, header2, source2}; + long long headerLastModifiedTime = 100; + long long headerLastModifiedTime2 = 110; + long long header2LastModifiedTime = 300; + long long header2LastModifiedTime2 = 310; + long long sourceLastModifiedTime = 200; + long long source2LastModifiedTime = 400; }; TEST_F(FileStatusCache, CreateEntry) @@ -134,18 +137,24 @@ TEST_F(FileStatusCache, AskNewEntryReverseOrderAddedForLastModifiedTime) TEST_F(FileStatusCache, UpdateFile) { - auto oldLastModified = cache.lastModifiedTime(header); - touchFile(header); + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); + cache.lastModifiedTime(header); cache.update(header); - ASSERT_THAT(cache.lastModifiedTime(header), Gt(oldLastModified)); + ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2); } TEST_F(FileStatusCache, UpdateFileDoesNotChangeEntryCount) { + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); cache.lastModifiedTime(header); - touchFile(header); cache.update(header); @@ -154,11 +163,136 @@ TEST_F(FileStatusCache, UpdateFileDoesNotChangeEntryCount) TEST_F(FileStatusCache, UpdateFileForNonExistingEntry) { - touchFile(header); - cache.update(header); ASSERT_THAT(cache, SizeIs(0)); } +TEST_F(FileStatusCache, UpdateFiles) +{ + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(header2))) + .Times(2) + .WillOnce(Return(header2LastModifiedTime)) + .WillOnce(Return(header2LastModifiedTime2)); + cache.lastModifiedTime(header); + cache.lastModifiedTime(header2); + + cache.update(entries); + + ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2); + ASSERT_THAT(cache.lastModifiedTime(header2), header2LastModifiedTime2); } + +TEST_F(FileStatusCache, UpdateFilesDoesNotChangeEntryCount) +{ + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(header2))) + .Times(2) + .WillOnce(Return(header2LastModifiedTime)) + .WillOnce(Return(header2LastModifiedTime2)); + cache.lastModifiedTime(header); + cache.lastModifiedTime(header2); + + cache.update(entries); + + ASSERT_THAT(cache, SizeIs(2)); +} + +TEST_F(FileStatusCache, UpdateFilesForNonExistingEntry) +{ + cache.update(entries); + + ASSERT_THAT(cache, SizeIs(0)); +} + +TEST_F(FileStatusCache, NewModifiedEntries) +{ + auto modifiedIds = cache.modified(entries); + + ASSERT_THAT(modifiedIds, entries); +} + +TEST_F(FileStatusCache, NoNewModifiedEntries) +{ + cache.modified(entries); + + auto modifiedIds = cache.modified(entries); + + ASSERT_THAT(modifiedIds, IsEmpty()); +} + +TEST_F(FileStatusCache, SomeNewModifiedEntries) +{ + cache.modified({source, header2}); + + auto modifiedIds = cache.modified(entries); + + ASSERT_THAT(modifiedIds, ElementsAre(header, source2)); +} + +TEST_F(FileStatusCache, SomeAlreadyExistingModifiedEntries) +{ + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(header2))) + .Times(2) + .WillOnce(Return(header2LastModifiedTime)) + .WillOnce(Return(header2LastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime)); + EXPECT_CALL(fileSystem, lastModified(Eq(source2))) + .Times(2) + .WillRepeatedly(Return(source2LastModifiedTime)); + cache.modified(entries); + + auto modifiedIds = cache.modified(entries); + + ASSERT_THAT(modifiedIds, ElementsAre(header, header2)); +} + +TEST_F(FileStatusCache, SomeAlreadyExistingAndSomeNewModifiedEntries) +{ + EXPECT_CALL(fileSystem, lastModified(Eq(header))).WillRepeatedly(Return(headerLastModifiedTime)); + EXPECT_CALL(fileSystem, lastModified(Eq(header2))) + .Times(2) + .WillOnce(Return(header2LastModifiedTime)) + .WillOnce(Return(header2LastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime)); + EXPECT_CALL(fileSystem, lastModified(Eq(source2))).WillRepeatedly(Return(source2LastModifiedTime)); + cache.modified({source, header2}); + + auto modifiedIds = cache.modified(entries); + + ASSERT_THAT(modifiedIds, ElementsAre(header, header2, source2)); +} + +TEST_F(FileStatusCache, TimeIsUpdatedForSomeAlreadyExistingModifiedEntries) +{ + EXPECT_CALL(fileSystem, lastModified(Eq(header))) + .Times(2) + .WillOnce(Return(headerLastModifiedTime)) + .WillOnce(Return(headerLastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(header2))) + .Times(2) + .WillOnce(Return(header2LastModifiedTime)) + .WillOnce(Return(header2LastModifiedTime2)); + EXPECT_CALL(fileSystem, lastModified(Eq(source))).Times(2).WillRepeatedly(Return(sourceLastModifiedTime)); + EXPECT_CALL(fileSystem, lastModified(Eq(source2))) + .Times(2) + .WillRepeatedly(Return(source2LastModifiedTime)); + cache.modified(entries); + + cache.modified(entries); + + ASSERT_THAT(cache.lastModifiedTime(header), headerLastModifiedTime2); +} + +} // namespace diff --git a/tests/unit/unittest/gtest-creator-printing.cpp b/tests/unit/unittest/gtest-creator-printing.cpp index 7ac4a676e3e..0ab962dfa55 100644 --- a/tests/unit/unittest/gtest-creator-printing.cpp +++ b/tests/unit/unittest/gtest-creator-printing.cpp @@ -361,7 +361,7 @@ std::ostream &operator<<(std::ostream &out, const WatcherEntry &entry) { out << "(" << entry.id << ", " - << entry.pathId + << entry.filePathId << ")"; return out; diff --git a/tests/unit/unittest/mockfilepathcaching.h b/tests/unit/unittest/mockfilepathcaching.h index 1b22e910eee..ca7960513eb 100644 --- a/tests/unit/unittest/mockfilepathcaching.h +++ b/tests/unit/unittest/mockfilepathcaching.h @@ -36,5 +36,11 @@ public: ClangBackEnd::FilePathId (ClangBackEnd::FilePathView filePath)); MOCK_CONST_METHOD1(filePath, ClangBackEnd::FilePath (ClangBackEnd::FilePathId filePathId)); + MOCK_CONST_METHOD1(directoryPathId, + ClangBackEnd::DirectoryPathId(Utils::SmallStringView directoryPath)); + MOCK_CONST_METHOD1(directoryPath, + Utils::PathString(ClangBackEnd::DirectoryPathId directoryPathId)); + MOCK_CONST_METHOD1(directoryPathId, + ClangBackEnd::DirectoryPathId(ClangBackEnd::FilePathId filePathId)); }; diff --git a/tests/unit/unittest/mockfilesystem.h b/tests/unit/unittest/mockfilesystem.h new file mode 100644 index 00000000000..688edbcae57 --- /dev/null +++ b/tests/unit/unittest/mockfilesystem.h @@ -0,0 +1,37 @@ +/**************************************************************************** +** +** 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 "googletest.h" + +#include + +class MockFileSystem : public ClangBackEnd::FileSystemInterface +{ +public: + MOCK_CONST_METHOD1(directoryEntries, ClangBackEnd::FilePathIds(const QString &directoryPath)); + MOCK_CONST_METHOD1(lastModified, long long(ClangBackEnd::FilePathId filePathId)); +}; diff --git a/tests/unit/unittest/mockqfilesystemwatcher.h b/tests/unit/unittest/mockqfilesystemwatcher.h index 1bb90b2c37d..00f4c600b43 100644 --- a/tests/unit/unittest/mockqfilesystemwatcher.h +++ b/tests/unit/unittest/mockqfilesystemwatcher.h @@ -41,4 +41,5 @@ public: signals: void fileChanged(const QString &); + void directoryChanged(const QString &); }; diff --git a/tests/unit/unittest/mocksqlitereadstatement.cpp b/tests/unit/unittest/mocksqlitereadstatement.cpp index b0df1ab75dc..1e800e2be15 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.cpp +++ b/tests/unit/unittest/mocksqlitereadstatement.cpp @@ -137,6 +137,12 @@ MockSqliteReadStatement::value(const int &directoryId, const Utils::SmallSt return valueReturnInt32(directoryId, text); } +template<> +Utils::optional MockSqliteReadStatement::value(const int &value) +{ + return valueReturnInt32(value); +} + template <> Utils::optional MockSqliteReadStatement::value(const int &sourceId) diff --git a/tests/unit/unittest/mocksqlitereadstatement.h b/tests/unit/unittest/mocksqlitereadstatement.h index d952c90dec0..191c38accdd 100644 --- a/tests/unit/unittest/mocksqlitereadstatement.h +++ b/tests/unit/unittest/mocksqlitereadstatement.h @@ -92,11 +92,11 @@ public: MOCK_METHOD1(valueReturnInt32, Utils::optional(Utils::SmallStringView)); - MOCK_METHOD2(valueReturnInt32, - Utils::optional(int, Utils::SmallStringView)); + MOCK_METHOD2(valueReturnInt32, Utils::optional(int, Utils::SmallStringView)); - MOCK_METHOD1(valueReturnInt64, - Utils::optional(int)); + MOCK_METHOD1(valueReturnInt32, Utils::optional(int)); + + MOCK_METHOD1(valueReturnInt64, Utils::optional(int)); MOCK_METHOD1(valueReturnPathString, Utils::optional(int)); @@ -244,6 +244,9 @@ template <> Utils::optional MockSqliteReadStatement::value(const int&, const Utils::SmallStringView&); +template<> +Utils::optional MockSqliteReadStatement::value(const int &); + template <> Utils::optional MockSqliteReadStatement::value(const ClangBackEnd::FilePathId&); diff --git a/tests/unit/unittest/symbolindexer-test.cpp b/tests/unit/unittest/symbolindexer-test.cpp index 2db11dc5fbe..0bfcf131880 100644 --- a/tests/unit/unittest/symbolindexer-test.cpp +++ b/tests/unit/unittest/symbolindexer-test.cpp @@ -28,6 +28,7 @@ #include "mockbuilddependenciesstorage.h" #include "mockclangpathwatcher.h" #include "mockfilepathcaching.h" +#include "mockfilesystem.h" #include "mockmodifiedtimechecker.h" #include "mockprecompiledheaderstorage.h" #include "mockprojectpartsstorage.h" @@ -149,13 +150,6 @@ protected: data.reset(); } - void touchFile(FilePathId filePathId) - { - std::ofstream ostream(std::string(filePathCache.filePath(filePathId)), std::ios::binary); - ostream.write("\n", 1); - ostream.close(); - } - FilePathId filePathId(Utils::SmallStringView path) const { return filePathCache.filePathId(ClangBackEnd::FilePathView(path)); @@ -249,7 +243,8 @@ protected: NiceMock mockPrecompiledHeaderStorage; NiceMock mockProjectPartsStorage; NiceMock mockPathWatcher; - ClangBackEnd::FileStatusCache fileStatusCache{filePathCache}; + NiceMock mockFileSystem; + ClangBackEnd::FileStatusCache fileStatusCache{mockFileSystem}; ClangBackEnd::GeneratedFiles generatedFiles; Manager collectorManger{generatedFiles}; NiceMock> mockSetProgressCallback; @@ -1664,13 +1659,12 @@ TEST_F(SymbolIndexer, DISABLED_DontReparseInUpdateProjectPartsIfDefinesAreTheSam TEST_F(SymbolIndexer, PathsChangedUpdatesFileStatusCache) { auto sourceId = filePathId(TESTDATA_DIR "/symbolindexer_pathChanged.cpp"); - auto oldLastModified = fileStatusCache.lastModifiedTime(sourceId); - touchFile(sourceId); + ON_CALL(mockFileSystem, lastModified(Eq(sourceId))).WillByDefault(Return(65)); ON_CALL(mockSymbolStorage, fetchDependentSourceIds(_)).WillByDefault(Return(FilePathIds{sourceId})); indexer.pathsChanged({sourceId}); - ASSERT_THAT(fileStatusCache.lastModifiedTime(sourceId), Gt(oldLastModified)); + ASSERT_THAT(fileStatusCache.lastModifiedTime(sourceId), 65); } TEST_F(SymbolIndexer, GetUpdatableFilePathIdsIfCompilerMacrosAreDifferent) @@ -1706,6 +1700,7 @@ TEST_F(SymbolIndexer, GetNoUpdatableFilePathIdsIfArtefactsAreTheSame) TEST_F(SymbolIndexer, OutdatedFilesPassUpdatableFilePathIds) { + ON_CALL(mockFileSystem, lastModified(Eq(main1PathId))).WillByDefault(Return(65)); indexer.pathsChanged({main1PathId}); ON_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(A())) .WillByDefault(Return(artefact)); diff --git a/tests/unit/unittest/unittest.pro b/tests/unit/unittest/unittest.pro index 63980ab0554..d9cef683e7c 100644 --- a/tests/unit/unittest/unittest.pro +++ b/tests/unit/unittest/unittest.pro @@ -46,7 +46,7 @@ QMAKE_SUBSTITUTES += cpptoolsjson DEFINES += CPPTOOLS_JSON=\"R\\\"xxx($${cpptoolsjson.output})xxx\\\"\" SOURCES += \ - changedfilepathcompressor-test.cpp \ + directorypathcompressor-test.cpp \ clangpathwatcher-test.cpp \ clangqueryexamplehighlightmarker-test.cpp \ clangqueryhighlightmarker-test.cpp \ @@ -175,7 +175,7 @@ SOURCES += \ translationunitupdater-test.cpp \ unsavedfiles-test.cpp \ unsavedfile-test.cpp \ - utf8positionfromlinecolumn-test.cpp \ + utf8positionfromlinecolumn-test.cpp } !isEmpty(LIBTOOLING_LIBS) { @@ -226,6 +226,7 @@ HEADERS += \ mockclangcodemodelserver.h \ mockclangpathwatcher.h \ mockclangpathwatchernotifier.h \ + mockfilesystem.h \ mockpchcreator.h \ mockpchmanagerclient.h \ mockpchmanagernotifier.h \ From cbfd9dc16b41176ce5a79bd9e11a336e853573a0 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Mon, 17 Jun 2019 18:17:49 +0200 Subject: [PATCH 54/59] Clang: Update ModifiedTimeChecker in SymbolIndexer If a watched file changed we should update the ModifiedTimeChecker too. Change-Id: Ie43f5cf5b6dd4ddb1383168a1326add21f6e3e9d Reviewed-by: Tim Jenssen --- src/libs/clangsupport/modifiedtimechecker.h | 3 ++- src/libs/clangsupport/modifiedtimecheckerinterface.h | 1 + .../clangrefactoringbackend/source/symbolindexer.cpp | 6 ++++-- src/tools/clangrefactoringbackend/source/symbolindexer.h | 2 +- tests/unit/unittest/mockmodifiedtimechecker.h | 2 ++ tests/unit/unittest/symbolindexer-test.cpp | 9 +++++++++ 6 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/libs/clangsupport/modifiedtimechecker.h b/src/libs/clangsupport/modifiedtimechecker.h index c9b62c4673c..9247933b184 100644 --- a/src/libs/clangsupport/modifiedtimechecker.h +++ b/src/libs/clangsupport/modifiedtimechecker.h @@ -25,6 +25,7 @@ #pragma once +#include "clangpathwatcher.h" #include "filepathcachinginterface.h" #include "modifiedtimecheckerinterface.h" @@ -54,7 +55,7 @@ public: return compareEntries(sourceEntries); } - void pathsChanged(const FilePathIds &filePathIds) + void pathsChanged(const FilePathIds &filePathIds) override { using SourceTimeStampReferences = std::vector>; diff --git a/src/libs/clangsupport/modifiedtimecheckerinterface.h b/src/libs/clangsupport/modifiedtimecheckerinterface.h index a0e79b0701e..b48c38869e5 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/tools/clangrefactoringbackend/source/symbolindexer.cpp b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp index 1ee61eb67c2..299088aa4b9 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) @@ -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; diff --git a/src/tools/clangrefactoringbackend/source/symbolindexer.h b/src/tools/clangrefactoringbackend/source/symbolindexer.h index 64b442bf33d..d969cd80264 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 #include #include #include diff --git a/tests/unit/unittest/mockmodifiedtimechecker.h b/tests/unit/unittest/mockmodifiedtimechecker.h index bf101988b16..1fa3dd497ca 100644 --- a/tests/unit/unittest/mockmodifiedtimechecker.h +++ b/tests/unit/unittest/mockmodifiedtimechecker.h @@ -35,6 +35,7 @@ class MockSourceEntriesModifiedTimeChecker public: MOCK_CONST_METHOD1(isUpToDate, bool (const ClangBackEnd::SourceEntries &sourceEntries)); + MOCK_METHOD1(pathsChanged, void(const ClangBackEnd::FilePathIds &filePathIds)); }; class MockSourceTimeStampsModifiedTimeChecker @@ -42,4 +43,5 @@ class MockSourceTimeStampsModifiedTimeChecker { public: MOCK_CONST_METHOD1(isUpToDate, bool(const ClangBackEnd::SourceTimeStamps &sourceTimeStamps)); + MOCK_METHOD1(pathsChanged, void(const ClangBackEnd::FilePathIds &filePathIds)); }; diff --git a/tests/unit/unittest/symbolindexer-test.cpp b/tests/unit/unittest/symbolindexer-test.cpp index 0bfcf131880..c7831d099f2 100644 --- a/tests/unit/unittest/symbolindexer-test.cpp +++ b/tests/unit/unittest/symbolindexer-test.cpp @@ -1667,6 +1667,15 @@ TEST_F(SymbolIndexer, PathsChangedUpdatesFileStatusCache) ASSERT_THAT(fileStatusCache.lastModifiedTime(sourceId), 65); } +TEST_F(SymbolIndexer, PathsChangedCallsModifiedTimeChecker) +{ + auto sourceId = filePathId(TESTDATA_DIR "/symbolindexer_pathChanged.cpp"); + + EXPECT_CALL(mockModifiedTimeChecker, pathsChanged(ElementsAre(sourceId))); + + indexer.pathsChanged({sourceId}); +} + TEST_F(SymbolIndexer, GetUpdatableFilePathIdsIfCompilerMacrosAreDifferent) { ON_CALL(mockProjectPartsStorage, fetchProjectPartArtefact(A())) From e031ada154ba94e13af104a1e32be0f94754ba12 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 18 Jun 2019 18:26:27 +0200 Subject: [PATCH 55/59] Clang: Watch only PCH sources We watched all sources but we do not want to watch the sources of the project part because they are not used to build a PCH. Change-Id: I700cd6077fc54230c9be94d620043cf3f10cf9ea Reviewed-by: Tim Jenssen --- .../refactoringdatabaseinitializer.h | 4 ++-- .../source/builddependenciesstorage.h | 11 +++++---- .../builddependenciesstorageinterface.h | 2 +- .../source/pchcreator.cpp | 9 ++++--- .../source/usedmacrofilter.h | 5 +++- .../source/symbolindexer.cpp | 2 +- .../builddependenciesstorage-test.cpp | 4 ++-- .../unittest/mockbuilddependenciesstorage.h | 2 +- tests/unit/unittest/pchcreator-test.cpp | 24 +++++++++++++------ tests/unit/unittest/pchtaskgenerator-test.cpp | 2 +- .../refactoringdatabaseinitializer-test.cpp | 12 ++++++---- tests/unit/unittest/symbolindexer-test.cpp | 2 +- tests/unit/unittest/usedmacrofilter-test.cpp | 7 +----- 13 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/libs/clangsupport/refactoringdatabaseinitializer.h b/src/libs/clangsupport/refactoringdatabaseinitializer.h index a7058d32041..6042264a5da 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/tools/clangpchmanagerbackend/source/builddependenciesstorage.h b/src/tools/clangpchmanagerbackend/source/builddependenciesstorage.h index 8c8c51a6cd2..187a27cf89e 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(1024, - projectPartId.projectPathId); + return fetchPchSourcesStatement.template values(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 1166564b329..a1c20811aa6 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 cd7b492868d..15c9a195470 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 9db3c683b90..2d16b484a1b 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/symbolindexer.cpp b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp index 299088aa4b9..4f3a6f172f3 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexer.cpp +++ b/src/tools/clangrefactoringbackend/source/symbolindexer.cpp @@ -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(); } diff --git a/tests/unit/unittest/builddependenciesstorage-test.cpp b/tests/unit/unittest/builddependenciesstorage-test.cpp index 052ed452f09..d14b94fe30d 100644 --- a/tests/unit/unittest/builddependenciesstorage-test.cpp +++ b/tests/unit/unittest/builddependenciesstorage-test.cpp @@ -70,7 +70,7 @@ protected: MockSqliteWriteStatement &updatePchCreationTimeStampStatement = storage.updatePchCreationTimeStampStatement; MockSqliteWriteStatement &deleteAllProjectPartsFilesWithProjectPartNameStatement = storage.deleteAllProjectPartsFilesWithProjectPartNameStatement; - MockSqliteReadStatement &fetchProjectPartsFilesStatement = storage.fetchProjectPartsFilesStatement; + MockSqliteReadStatement &fetchProjectPartsFilesStatement = storage.fetchPchSourcesStatement; }; TEST_F(BuildDependenciesStorage, ConvertStringsToJson) @@ -238,7 +238,7 @@ TEST_F(BuildDependenciesStorage, FetchSources) ClangBackEnd::FilePathIds result{3, 5, 7}; EXPECT_CALL(fetchProjectPartsFilesStatement, valuesReturnFilePathIds(_, 22)).WillOnce(Return(result)); - auto sources = storage.fetchSources(22); + auto sources = storage.fetchPchSources(22); ASSERT_THAT(sources, result); } diff --git a/tests/unit/unittest/mockbuilddependenciesstorage.h b/tests/unit/unittest/mockbuilddependenciesstorage.h index a4d34e481dc..dd2850d88cc 100644 --- a/tests/unit/unittest/mockbuilddependenciesstorage.h +++ b/tests/unit/unittest/mockbuilddependenciesstorage.h @@ -51,7 +51,7 @@ public: ClangBackEnd::ProjectPartId(Utils::SmallStringView projectPartName)); MOCK_METHOD2(updatePchCreationTimeStamp, void(long long pchCreationTimeStamp, ClangBackEnd::ProjectPartId projectPartId)); - MOCK_CONST_METHOD1(fetchSources, + MOCK_CONST_METHOD1(fetchPchSources, ClangBackEnd::FilePathIds(ClangBackEnd::ProjectPartId projectPartId)); }; diff --git a/tests/unit/unittest/pchcreator-test.cpp b/tests/unit/unittest/pchcreator-test.cpp index 0cf84a983dd..04e198eec75 100644 --- a/tests/unit/unittest/pchcreator-test.cpp +++ b/tests/unit/unittest/pchcreator-test.cpp @@ -226,16 +226,21 @@ TEST_F(PchCreatorVerySlowTest, SourcesAreWatchedAfterSucess) creator.doInMainThreadAfterFinished(); } -TEST_F(PchCreatorVerySlowTest, SourcesAreNotWatchedAfterFail) +TEST_F(PchCreatorVerySlowTest, SourcesAreWatchedAfterFail) { pchTask1.systemIncludeSearchPaths = {}; pchTask1.projectIncludeSearchPaths = {}; creator.generatePch(std::move(pchTask1)); EXPECT_CALL(mockClangPathWatcher, - updateIdPaths( - ElementsAre(AllOf(Field(&ClangBackEnd::IdPaths::id, 1), - Field(&ClangBackEnd::IdPaths::filePathIds, IsEmpty()))))); + updateIdPaths(ElementsAre(AllOf( + Field(&ClangBackEnd::IdPaths::id, 1), + Field(&ClangBackEnd::IdPaths::filePathIds, + UnorderedElementsAre( + id(TESTDATA_DIR "/builddependencycollector/project/header2.h"), + id(TESTDATA_DIR "/builddependencycollector/external/external1.h"), + id(TESTDATA_DIR "/builddependencycollector/external/external2.h"), + id(TESTDATA_DIR "/builddependencycollector/project/main2.cpp"))))))); creator.doInMainThreadAfterFinished(); } @@ -337,9 +342,14 @@ TEST_F(PchCreatorSlowTest, NoIncludesInTheMainThreadCalls) Field(&ClangBackEnd::PrecompiledHeadersUpdatedMessage::projectPartIds, ElementsAre(Eq(creator.projectPartPch().projectPartId))))); EXPECT_CALL(mockClangPathWatcher, - updateIdPaths( - ElementsAre(AllOf(Field(&ClangBackEnd::IdPaths::id, 1), - Field(&ClangBackEnd::IdPaths::filePathIds, IsEmpty()))))); + updateIdPaths(ElementsAre(AllOf( + Field(&ClangBackEnd::IdPaths::id, 1), + Field(&ClangBackEnd::IdPaths::filePathIds, + UnorderedElementsAre( + id(TESTDATA_DIR "/builddependencycollector/project/header2.h"), + id(TESTDATA_DIR "/builddependencycollector/external/external1.h"), + id(TESTDATA_DIR "/builddependencycollector/external/external2.h"), + id(TESTDATA_DIR "/builddependencycollector/project/main2.cpp"))))))); EXPECT_CALL(mockBuildDependenciesStorage, updatePchCreationTimeStamp(Gt(0), Eq(1))); creator.doInMainThreadAfterFinished(); diff --git a/tests/unit/unittest/pchtaskgenerator-test.cpp b/tests/unit/unittest/pchtaskgenerator-test.cpp index 8f5f0754af6..cb9df32235c 100644 --- a/tests/unit/unittest/pchtaskgenerator-test.cpp +++ b/tests/unit/unittest/pchtaskgenerator-test.cpp @@ -117,7 +117,7 @@ TEST_F(PchTaskGenerator, AddProjectParts) &PchTaskSet::project, AllOf(Field(&PchTask::projectPartIds, ElementsAre(ProjectPartId{1})), Field(&PchTask::includes, ElementsAre(3)), - Field(&PchTask::sources, ElementsAre(1, 2, 3, 4, 5)), + Field(&PchTask::sources, ElementsAre(1, 3, 4, 5)), Field(&PchTask::compilerMacros, ElementsAre(CompilerMacro{"YI", "1", 1}, CompilerMacro{"SAN", "3", 3})), Field(&PchTask::systemIncludeSearchPaths, diff --git a/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp b/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp index 71770603110..3af0fc8cfec 100644 --- a/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp +++ b/tests/unit/unittest/refactoringdatabaseinitializer-test.cpp @@ -107,7 +107,10 @@ TEST_F(RefactoringDatabaseInitializer, AddProjectPartsFilesTable) "sourceId INTEGER, sourceType INTEGER, pchCreationTimeStamp INTEGER, " "hasMissingIncludes INTEGER)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectPartsFiles_sourceId_projectPartId ON projectPartsFiles(sourceId, projectPartId)"))); - EXPECT_CALL(mockDatabase, execute(Eq("CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId ON projectPartsFiles(projectPartId)"))); + EXPECT_CALL(mockDatabase, + execute(Eq( + "CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId_sourceType " + "ON projectPartsFiles(projectPartId, sourceType)"))); initializer.createProjectPartsFilesTable(); } @@ -247,9 +250,10 @@ TEST_F(RefactoringDatabaseInitializer, CreateInTheContructor) execute( Eq("CREATE UNIQUE INDEX IF NOT EXISTS index_projectPartsFiles_sourceId_projectPartId " "ON projectPartsFiles(sourceId, projectPartId)"))); - EXPECT_CALL(mockDatabase, - execute(Eq("CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId ON " - "projectPartsFiles(projectPartId)"))); + EXPECT_CALL( + mockDatabase, + execute(Eq("CREATE INDEX IF NOT EXISTS index_projectPartsFiles_projectPartId_sourceType ON " + "projectPartsFiles(projectPartId, sourceType)"))); EXPECT_CALL(mockDatabase, execute(Eq("CREATE TABLE IF NOT EXISTS usedMacros(usedMacroId INTEGER PRIMARY KEY, " "sourceId INTEGER, macroName TEXT)"))); diff --git a/tests/unit/unittest/symbolindexer-test.cpp b/tests/unit/unittest/symbolindexer-test.cpp index c7831d099f2..16e0fc81b09 100644 --- a/tests/unit/unittest/symbolindexer-test.cpp +++ b/tests/unit/unittest/symbolindexer-test.cpp @@ -923,7 +923,7 @@ TEST_F(SymbolIndexer, SourcesAreWatched) InSequence s; FilePathIds sourcePathIds{4, 6, 8}; - EXPECT_CALL(mockBuildDependenciesStorage, fetchSources(projectPart1.projectPartId)) + EXPECT_CALL(mockBuildDependenciesStorage, fetchPchSources(projectPart1.projectPartId)) .WillOnce(Return(sourcePathIds)); EXPECT_CALL(mockPathWatcher, updateIdPaths(ElementsAre(AllOf(Field(&IdPaths::id, projectPart1.projectPartId), diff --git a/tests/unit/unittest/usedmacrofilter-test.cpp b/tests/unit/unittest/usedmacrofilter-test.cpp index 788a443eee2..85e1bb59b50 100644 --- a/tests/unit/unittest/usedmacrofilter-test.cpp +++ b/tests/unit/unittest/usedmacrofilter-test.cpp @@ -98,12 +98,7 @@ TEST_F(UsedMacroFilter, Sources) ClangBackEnd::UsedMacroFilter filter(sources, usedMacros, compileMacros); ASSERT_THAT(filter.sources, - ElementsAre(FilePathId{1}, - FilePathId{2}, - FilePathId{3}, - FilePathId{4}, - FilePathId{5}, - FilePathId{6})); + ElementsAre(FilePathId{2}, FilePathId{3}, FilePathId{4}, FilePathId{5})); } TEST_F(UsedMacroFilter, SystemUsedMacros) From e777ad57c547abe31b457a95a9dc0b5e39891a79 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Wed, 19 Jun 2019 15:34:24 +0200 Subject: [PATCH 56/59] Clang: Add reset to ModifiedTimeChecker We can reset some file once to flag a file dirty if the included file has changed. Change-Id: I8763bb80f65882fba4e70057f569234e77097927 Reviewed-by: Tim Jenssen --- src/libs/clangsupport/clangsupport-lib.pri | 1 + src/libs/clangsupport/filestatuscache.cpp | 54 ++--- src/libs/clangsupport/modifiedtimechecker.h | 185 ++++++------------ src/libs/clangsupport/set_algorithm.h | 101 ++++++++++ src/libs/clangsupport/sourceentry.h | 4 + .../clangpchmanagerbackendmain.cpp | 7 +- .../source/symbolindexing.h | 7 +- .../unittest/modifiedtimechecker-test.cpp | 127 +++++++++--- 8 files changed, 279 insertions(+), 207 deletions(-) create mode 100644 src/libs/clangsupport/set_algorithm.h diff --git a/src/libs/clangsupport/clangsupport-lib.pri b/src/libs/clangsupport/clangsupport-lib.pri index 9b6bbdf3ee5..dc66408e581 100644 --- a/src/libs/clangsupport/clangsupport-lib.pri +++ b/src/libs/clangsupport/clangsupport-lib.pri @@ -157,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 \ diff --git a/src/libs/clangsupport/filestatuscache.cpp b/src/libs/clangsupport/filestatuscache.cpp index 480b7455259..e8ed50a8335 100644 --- a/src/libs/clangsupport/filestatuscache.cpp +++ b/src/libs/clangsupport/filestatuscache.cpp @@ -26,6 +26,8 @@ #include "filestatuscache.h" #include "filesystem.h" +#include + #include #include @@ -51,49 +53,15 @@ void FileStatusCache::update(FilePathId filePathId) found->lastModified = m_fileSystem.lastModified(filePathId); } -namespace { -template -void set_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)) - callable(*first1++); - ++first2; - } - } -} - -template -void set_difference_call(InputIt1 first1, InputIt1 last1, InputIt2 first2, InputIt2 last2, Callable callable) -{ - while (first1 != last1) { - if (first2 == last2) { - std::for_each(first1, last1, callable); - return; - } - if (*first1 < *first2) { - callable(*first1++); - } else { - if (!(*first2 < *first1)) - ++first1; - ++first2; - } - } -} -} // namespace - void FileStatusCache::update(FilePathIds filePathIds) { - set_intersection_call(m_cacheEntries.begin(), + std::set_intersection(m_cacheEntries.begin(), m_cacheEntries.end(), filePathIds.begin(), filePathIds.end(), - [&](auto &entry) { + make_iterator([&](auto &entry) { entry.lastModified = m_fileSystem.lastModified(entry.filePathId); - }); + })); } FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const @@ -101,30 +69,30 @@ FilePathIds FileStatusCache::modified(FilePathIds filePathIds) const FilePathIds modifiedFilePathIds; modifiedFilePathIds.reserve(filePathIds.size()); - set_intersection_call(m_cacheEntries.begin(), + std::set_intersection(m_cacheEntries.begin(), m_cacheEntries.end(), filePathIds.begin(), filePathIds.end(), - [&](auto &entry) { + 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()); - set_difference_call(filePathIds.begin(), + std::set_difference(filePathIds.begin(), filePathIds.end(), m_cacheEntries.begin(), m_cacheEntries.end(), - [&](FilePathId newFilePathId) { + make_iterator([&](FilePathId newFilePathId) { newEntries.emplace_back(newFilePathId, m_fileSystem.lastModified(newFilePathId)); modifiedFilePathIds.push_back(newFilePathId); - }); + })); if (newEntries.size()) { Internal::FileStatusCacheEntries mergedEntries; diff --git a/src/libs/clangsupport/modifiedtimechecker.h b/src/libs/clangsupport/modifiedtimechecker.h index 9247933b184..00e8e3ebacf 100644 --- a/src/libs/clangsupport/modifiedtimechecker.h +++ b/src/libs/clangsupport/modifiedtimechecker.h @@ -25,24 +25,23 @@ #pragma once -#include "clangpathwatcher.h" -#include "filepathcachinginterface.h" +#include "filesysteminterface.h" #include "modifiedtimecheckerinterface.h" +#include "set_algorithm.h" #include #include namespace ClangBackEnd { + template class ModifiedTimeChecker final : public ModifiedTimeCheckerInterface { using SourceEntry = typename SourceEntries::value_type; public: - using GetModifiedTime = std::function; - ModifiedTimeChecker(GetModifiedTime &getModifiedTime, FilePathCachingInterface &filePathCache) - : m_getModifiedTime(getModifiedTime) - , m_filePathCache(filePathCache) + ModifiedTimeChecker(FileSystemInterface &fileSystem) + : m_fileSystem(fileSystem) {} bool isUpToDate(const SourceEntries &sourceEntries) const @@ -52,165 +51,101 @@ public: updateCurrentSourceTimeStamps(sourceEntries); - return compareEntries(sourceEntries); + return compareEntries(sourceEntries) && notReseted(sourceEntries); } void pathsChanged(const FilePathIds &filePathIds) override { - using SourceTimeStampReferences = std::vector>; - - 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); + })); + } - for (SourceTimeStamp &sourceTimeStamp : timeStampsToUpdate) { - sourceTimeStamp.timeStamp = m_getModifiedTime( - m_filePathCache.filePath(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)); + + 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{}); - - SourceTimeStamps newTimeStamps; - newTimeStamps.reserve(newSourceEntries.size()); - - std::transform(newSourceEntries.begin(), - newSourceEntries.end(), - std::back_inserter(newTimeStamps), - [](SourceEntry entry) { - return SourceTimeStamp{entry.sourceId, {}}; - }); + 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; + }); return newTimeStamps; } + bool notReseted(const SourceEntries &sourceEntries) const + { + auto oldSize = m_resetFilePathIds.size(); + FilePathIds newResetFilePathIds; + newResetFilePathIds.reserve(newResetFilePathIds.size()); + + 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/set_algorithm.h b/src/libs/clangsupport/set_algorithm.h new file mode 100644 index 00000000000..39bd3a2055b --- /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 + +namespace ClangBackEnd { + +template +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 + helper &operator=(T &&value) + { + (*m_callable)(std::forward(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 +function_output_iterator make_iterator(const Callable &callable) +{ + return function_output_iterator(callable); +} + +template +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 c593f1fcd3d..5e8769d14fd 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/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp index d9e7dd3175d..bcf05e5d042 100644 --- a/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp +++ b/src/tools/clangpchmanagerbackend/clangpchmanagerbackendmain.cpp @@ -214,12 +214,7 @@ struct Data // because we have a cycle dependency ClangBackEnd::BuildDependencyCollector buildDependencyCollector{filePathCache, generatedFiles, environment}; - std::function getModifiedTime{ - [&](ClangBackEnd::FilePathView path) -> TimeStamp { - return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); - }}; - ClangBackEnd::ModifiedTimeChecker modifiedTimeChecker{getModifiedTime, - filePathCache}; + ClangBackEnd::ModifiedTimeChecker modifiedTimeChecker{fileSystem}; ClangBackEnd::BuildDependenciesProvider buildDependencyProvider{buildDependencyStorage, modifiedTimeChecker, buildDependencyCollector, diff --git a/src/tools/clangrefactoringbackend/source/symbolindexing.h b/src/tools/clangrefactoringbackend/source/symbolindexing.h index bed279904dc..936a34af00c 100644 --- a/src/tools/clangrefactoringbackend/source/symbolindexing.h +++ b/src/tools/clangrefactoringbackend/source/symbolindexing.h @@ -148,12 +148,7 @@ private: FileStatusCache m_fileStatusCache{m_fileSytem}; SymbolsCollectorManager m_collectorManger; ProgressCounter m_progressCounter; - std::function getModifiedTime{ - [&](ClangBackEnd::FilePathView path) -> TimeStamp { - return QFileInfo(QString(path)).lastModified().toSecsSinceEpoch(); - }}; - ModifiedTimeChecker m_modifiedTimeChecker{getModifiedTime, - m_filePathCache}; + ModifiedTimeChecker m_modifiedTimeChecker{m_fileSytem}; SymbolIndexer m_indexer; SymbolIndexerTaskQueue m_indexerQueue; SymbolIndexerTaskScheduler m_indexerScheduler; diff --git a/tests/unit/unittest/modifiedtimechecker-test.cpp b/tests/unit/unittest/modifiedtimechecker-test.cpp index ff0c3c66d84..0e18228ac3c 100644 --- a/tests/unit/unittest/modifiedtimechecker-test.cpp +++ b/tests/unit/unittest/modifiedtimechecker-test.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "googletest.h" +#include "mockfilesystem.h" #include #include @@ -39,29 +40,24 @@ using ClangBackEnd::FilePathView; class ModifiedTimeChecker : public testing::Test { protected: - - ClangBackEnd::FilePathId id(const Utils::SmallStringView &path) const - { - return filePathCache.filePathId(ClangBackEnd::FilePathView{path}); - } - - ModifiedTimeChecker() { - ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1")))).WillByDefault(Return(50)); - ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2")))).WillByDefault(Return(30)); + ON_CALL(mockFileSystem, lastModified(Eq(1))).WillByDefault(Return(50)); + ON_CALL(mockFileSystem, lastModified(Eq(2))).WillByDefault(Return(30)); + ON_CALL(mockFileSystem, lastModified(Eq(3))).WillByDefault(Return(50)); + ON_CALL(mockFileSystem, lastModified(Eq(4))).WillByDefault(Return(30)); } - NiceMock> getModifiedTimeCallback; - Sqlite::Database database{":memory:", Sqlite::JournalMode::Memory}; - ClangBackEnd::RefactoringDatabaseInitializer databaseInitializer{database}; - ClangBackEnd::FilePathCaching filePathCache{database}; - decltype(getModifiedTimeCallback.AsStdFunction()) callback = getModifiedTimeCallback - .AsStdFunction(); - ClangBackEnd::ModifiedTimeChecker<> checker{callback, filePathCache}; - SourceEntries upToDateEntries = {{id("/path1"), SourceType::UserInclude, 100}, - {id("/path2"), SourceType::SystemInclude, 30}}; - SourceEntries notUpToDateEntries = {{id("/path1"), SourceType::UserInclude, 50}, - {id("/path2"), SourceType::SystemInclude, 20}}; + + NiceMock mockFileSystem; + ClangBackEnd::ModifiedTimeChecker<> checker{mockFileSystem}; + SourceEntries upToDateEntries = {{1, SourceType::UserInclude, 100}, + {2, SourceType::SystemInclude, 30}, + {3, SourceType::UserInclude, 100}, + {4, SourceType::SystemInclude, 30}}; + SourceEntries notUpToDateEntries = {{1, SourceType::UserInclude, 50}, + {2, SourceType::SystemInclude, 20}, + {3, SourceType::UserInclude, 100}, + {4, SourceType::SystemInclude, 30}}; }; TEST_F(ModifiedTimeChecker, IsUpToDate) @@ -71,6 +67,15 @@ TEST_F(ModifiedTimeChecker, IsUpToDate) ASSERT_TRUE(isUpToDate); } +TEST_F(ModifiedTimeChecker, IsUpToDateSecondRun) +{ + checker.isUpToDate(upToDateEntries); + + auto isUpToDate = checker.isUpToDate(upToDateEntries); + + ASSERT_TRUE(isUpToDate); +} + TEST_F(ModifiedTimeChecker, IsNotUpToDateIfSourceEntriesAreEmpty) { auto isUpToDate = checker.isUpToDate({}); @@ -80,7 +85,16 @@ TEST_F(ModifiedTimeChecker, IsNotUpToDateIfSourceEntriesAreEmpty) TEST_F(ModifiedTimeChecker, IsNotUpToDate) { - auto isUpToDate = checker.isUpToDate({}); + auto isUpToDate = checker.isUpToDate(notUpToDateEntries); + + ASSERT_FALSE(isUpToDate); +} + +TEST_F(ModifiedTimeChecker, IsNotUpToDateSecondRun) +{ + checker.isUpToDate(notUpToDateEntries); + + auto isUpToDate = checker.isUpToDate(notUpToDateEntries); ASSERT_FALSE(isUpToDate); } @@ -89,21 +103,80 @@ TEST_F(ModifiedTimeChecker, PathChangesUpdatesTimeStamps) { checker.isUpToDate(upToDateEntries); - EXPECT_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1")))); - EXPECT_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2")))); + EXPECT_CALL(mockFileSystem, lastModified(Eq(2))); + EXPECT_CALL(mockFileSystem, lastModified(Eq(3))); - checker.pathsChanged({id(FilePathView("/path1")), id(FilePathView("/path2")), id(FilePathView("/path3"))}); + checker.pathsChanged({2, 3}); } TEST_F(ModifiedTimeChecker, IsNotUpToDateAnyMoreAfterUpdating) { checker.isUpToDate(upToDateEntries); - ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path1")))).WillByDefault(Return(120)); - ON_CALL(getModifiedTimeCallback, Call(Eq(FilePathView("/path2")))).WillByDefault(Return(30)); + ON_CALL(mockFileSystem, lastModified(Eq(1))).WillByDefault(Return(120)); + ON_CALL(mockFileSystem, lastModified(Eq(2))).WillByDefault(Return(30)); - checker.pathsChanged({id(FilePathView("/path1")), id(FilePathView("/path2")), id(FilePathView("/path3"))}); + checker.pathsChanged({1, 2, 3}); ASSERT_FALSE(checker.isUpToDate(upToDateEntries)); } +TEST_F(ModifiedTimeChecker, Reset) +{ + checker.isUpToDate(upToDateEntries); + + checker.reset({2, 3}); + + ASSERT_FALSE(checker.isUpToDate(upToDateEntries)); +} + +TEST_F(ModifiedTimeChecker, UpdateNonResetedId) +{ + checker.isUpToDate(upToDateEntries); + + checker.reset({2, 3}); + + ASSERT_TRUE(checker.isUpToDate({upToDateEntries[0]})); +} + +TEST_F(ModifiedTimeChecker, ResetTwoTimes) +{ + checker.isUpToDate(upToDateEntries); + checker.reset({2, 3}); + + checker.reset({2, 3}); + + ASSERT_FALSE(checker.isUpToDate(upToDateEntries)); + ASSERT_TRUE(checker.isUpToDate(upToDateEntries)); +} + +TEST_F(ModifiedTimeChecker, ResetSecondUpdate) +{ + checker.isUpToDate(upToDateEntries); + checker.reset({2, 3}); + checker.isUpToDate(upToDateEntries); + + auto isUpToDate = checker.isUpToDate(upToDateEntries); + + ASSERT_TRUE(isUpToDate); +} + +TEST_F(ModifiedTimeChecker, ResetPartialUpdate) +{ + checker.isUpToDate(upToDateEntries); + checker.reset({2, 3}); + checker.isUpToDate({upToDateEntries[1]}); + + ASSERT_FALSE(checker.isUpToDate({upToDateEntries[2]})); +} + +TEST_F(ModifiedTimeChecker, ResetMoreIds) +{ + checker.isUpToDate(upToDateEntries); + checker.reset({2, 3}); + + checker.reset({1, 5}); + + ASSERT_FALSE(checker.isUpToDate({upToDateEntries[2]})); +} + } // namespace From 0b5d60db535179f361fb990851603417a557de86 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Wed, 26 Jun 2019 17:42:41 +0200 Subject: [PATCH 57/59] Squish: Update tst_git_local Change-Id: If3b3eb69a7927d0ecbc5d10afee6ab3fdfbd448b Reviewed-by: Christian Stenger --- tests/system/suite_tools/tst_git_local/test.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/suite_tools/tst_git_local/test.py b/tests/system/suite_tools/tst_git_local/test.py index d5617642f4a..8cbdfc42c69 100644 --- a/tests/system/suite_tools/tst_git_local/test.py +++ b/tests/system/suite_tools/tst_git_local/test.py @@ -146,9 +146,9 @@ def verifyClickCommit(): "Verifying whether diff editor contains pointless_header.h file.") test.verify(pointlessHeader not in diffOriginal, "Verifying whether original does not contain pointless_header.h file.") - test.verify("HEADERS += \\\n mainwindow.h \\\n pointless_header.h\n" in diffChanged, + test.verify("HEADERS += \\\n mainwindow.h \\\n pointless_header.h\n" in diffChanged, "Verifying whether diff editor has pointless_header.h listed in pro file.") - test.verify("HEADERS += \\\n mainwindow.h\n\n" in diffOriginal + test.verify("HEADERS += \\\n mainwindow.h\n\n" in diffOriginal and "pointless_header.h" not in diffOriginal, "Verifying whether original has no additional header in pro file.") test.verify(original.readOnly and changed.readOnly, @@ -173,7 +173,7 @@ def main(): % os.path.join(srcPath, projectName, ".git").replace("\\", "/") in str(vcsLog), "Has initialization of repo been logged:\n%s " % vcsLog) createLocalGitConfig(os.path.join(srcPath, projectName, ".git")) - commitMessages = [commit("Initial Commit", "Committed 5 files.")] + commitMessages = [commit("Initial Commit", "Committed 6 files.")] clickButton(waitForObject(":*Qt Creator.Clear_QToolButton")) headerName = "pointless_header.h" addCPlusPlusFile(headerName, "C++ Header File", projectName + ".pro", From f25408c4364c977c97511ccad1b61ecd7b3df872 Mon Sep 17 00:00:00 2001 From: Robert Loehning Date: Wed, 26 Jun 2019 12:18:12 +0200 Subject: [PATCH 58/59] Squish: Remove outdated code Change-Id: Ic4c3cf43aed15756aaeaa4a5e7ed5240bed2545c Reviewed-by: Christian Stenger --- tests/system/objects.map | 1 - .../system/suite_tools/tst_designer_goto_slot/test.py | 10 ++-------- 2 files changed, 2 insertions(+), 9 deletions(-) diff --git a/tests/system/objects.map b/tests/system/objects.map index b1989cbe7ad..c6064297c7e 100644 --- a/tests/system/objects.map +++ b/tests/system/objects.map @@ -182,7 +182,6 @@ :Select a Git Commit.workingDirectoryEdit_QLineEdit {type='Utils::FancyLineEdit' unnamed='1' visible='1' window=':Select a Git Commit_Git::Internal::ChangeSelectionDialog'} :Select a Git Commit_Git::Internal::ChangeSelectionDialog {name='Git__Internal__ChangeSelectionDialog' type='Git::Internal::ChangeSelectionDialog' visible='1' windowTitle='Select a Git Commit'} :Select signal.signalList_QTreeView {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeView' visible='1'} -:Select signal.signalList_QTreeWidget {container=':Go to slot.Select signal_QGroupBox' name='signalList' type='QTreeWidget' visible='1'} :Send to Codepaster.Cancel_QPushButton {text='Cancel' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.Description:_QLabel {name='descriptionLabel' text='Description:' type='QLabel' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} :Send to Codepaster.Paste_QPushButton {text='Paste' type='QPushButton' unnamed='1' visible='1' window=':Send to Codepaster_CodePaster::PasteView'} diff --git a/tests/system/suite_tools/tst_designer_goto_slot/test.py b/tests/system/suite_tools/tst_designer_goto_slot/test.py index eadea648aed..f1e47727db0 100644 --- a/tests/system/suite_tools/tst_designer_goto_slot/test.py +++ b/tests/system/suite_tools/tst_designer_goto_slot/test.py @@ -50,14 +50,8 @@ def main(): waitFor("macHackActivateContextMenuItem('Go to slot...', con[0])", 6000) else: activateItem(waitForObjectItem("{type='QMenu' unnamed='1' visible='1'}", "Go to slot...")) - try: - # Creator built with Qt <= 5.9 - signalWidgetObject = waitForObject(":Select signal.signalList_QTreeWidget", 5000) - signalName = con[2] - except: - # Creator built with Qt >= 5.10 - signalWidgetObject = waitForObject(":Select signal.signalList_QTreeView") - signalName = con[1] + "." + con[2] + signalWidgetObject = waitForObject(":Select signal.signalList_QTreeView") + signalName = con[1] + "." + con[2] waitForObjectItem(signalWidgetObject, signalName) clickItem(signalWidgetObject, signalName, 5, 5, 0, Qt.LeftButton) clickButton(waitForObject(":Go to slot.OK_QPushButton")) From a111f251261159b50e92d6866f2058c66b43e390 Mon Sep 17 00:00:00 2001 From: Christian Kandeler Date: Wed, 26 Jun 2019 17:05:14 +0200 Subject: [PATCH 59/59] Project import: Fix focus issue If the user presses return in the path chooser, and the directory does not contain a proper build, then a message box will come up, after which the focus might not be at the path chooser anymore, which breaks our (admittedly somewhat brittle) logic. So we now keep an explicit state that tells us whether the parent widget should be allowed to handle the return key or not. Amends 50dc5674d30e. Change-Id: Ia4643b57641fda591292d20e6883e1c8bc281c0f Reviewed-by: Christian Stenger --- src/plugins/projectexplorer/importwidget.cpp | 10 +++++++--- src/plugins/projectexplorer/importwidget.h | 3 ++- src/plugins/projectexplorer/targetsetuppage.cpp | 2 +- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/plugins/projectexplorer/importwidget.cpp b/src/plugins/projectexplorer/importwidget.cpp index eac2c44c945..33db7d48df2 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 8999bc117d0..5c63beecdb7 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/targetsetuppage.cpp b/src/plugins/projectexplorer/targetsetuppage.cpp index 0a750199fe7..b5d2bfe3de0 100644 --- a/src/plugins/projectexplorer/targetsetuppage.cpp +++ b/src/plugins/projectexplorer/targetsetuppage.cpp @@ -330,7 +330,7 @@ void TargetSetupPage::setProjectImporter(ProjectImporter *importer) bool TargetSetupPage::importLineEditHasFocus() const { - return m_importWidget->lineEditHasFocus(); + return m_importWidget->ownsReturnKey(); } void TargetSetupPage::setNoteText(const QString &text)